From 06377691d163036d190d9fc6f0722a7bd47d2962 Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 8 Oct 2025 00:03:45 +0200 Subject: [PATCH 01/20] GH-598: second QA fix: the main thing done --- .../scanners/payable_scanner/start_scan.rs | 5 +-- .../tx_templates/initial/retry.rs | 39 +++++++++++++++++-- 2 files changed, 37 insertions(+), 7 deletions(-) diff --git a/node/src/accountant/scanners/payable_scanner/start_scan.rs b/node/src/accountant/scanners/payable_scanner/start_scan.rs index 35cbd3ab2..9eda6b359 100644 --- a/node/src/accountant/scanners/payable_scanner/start_scan.rs +++ b/node/src/accountant/scanners/payable_scanner/start_scan.rs @@ -157,11 +157,8 @@ mod tests { let retrieve_payables_params = retrieve_payables_params_arc.lock().unwrap(); let expected_tx_templates = { let mut tx_template_1 = RetryTxTemplate::from(&failed_tx_1); - tx_template_1.base.amount_in_wei = - tx_template_1.base.amount_in_wei + payable_account_1.balance_wei; - + tx_template_1.base.amount_in_wei = payable_account_1.balance_wei; let tx_template_2 = RetryTxTemplate::from(&failed_tx_2); - RetryTxTemplates(vec![tx_template_1, tx_template_2]) }; assert_eq!( diff --git a/node/src/accountant/scanners/payable_scanner/tx_templates/initial/retry.rs b/node/src/accountant/scanners/payable_scanner/tx_templates/initial/retry.rs index 9990635cd..2e209c8d0 100644 --- a/node/src/accountant/scanners/payable_scanner/tx_templates/initial/retry.rs +++ b/node/src/accountant/scanners/payable_scanner/tx_templates/initial/retry.rs @@ -13,11 +13,11 @@ pub struct RetryTxTemplate { } impl RetryTxTemplate { - pub fn new(failed_tx: &FailedTx, payable_scan_amount_opt: Option) -> Self { + pub fn new(failed_tx: &FailedTx, updated_payable_balance_opt: Option) -> Self { let mut retry_template = RetryTxTemplate::from(failed_tx); - if let Some(payable_scan_amount) = payable_scan_amount_opt { - retry_template.base.amount_in_wei += payable_scan_amount; + if let Some(updated_payable_balance) = updated_payable_balance_opt { + retry_template.base.amount_in_wei = updated_payable_balance; } retry_template @@ -99,6 +99,39 @@ mod tests { use crate::accountant::scanners::payable_scanner::tx_templates::BaseTxTemplate; use crate::blockchain::test_utils::{make_address, make_tx_hash}; + #[test] + fn retry_tx_template_constructor_works() { + let receiver_address = make_address(42); + let amount_in_wei = 1_000_000; + let gas_price = 20_000_000_000; + let nonce = 123; + let tx_hash = make_tx_hash(789); + let failed_tx = FailedTx { + hash: tx_hash, + receiver_address, + amount_minor: amount_in_wei, + gas_price_minor: gas_price, + nonce, + timestamp: 1234567, + reason: FailureReason::PendingTooLong, + status: FailureStatus::RetryRequired, + }; + let fetched_balance_from_payable_table_opt_1 = None; + let fetched_balance_from_payable_table_opt_2 = Some(1_234_567); + + let result_1 = RetryTxTemplate::new(&failed_tx, fetched_balance_from_payable_table_opt_1); + let result_2 = RetryTxTemplate::new(&failed_tx, fetched_balance_from_payable_table_opt_2); + + let assert = |result: RetryTxTemplate, expected_amount_in_wei: u128| { + assert_eq!(result.base.receiver_address, receiver_address); + assert_eq!(result.base.amount_in_wei, expected_amount_in_wei); + assert_eq!(result.prev_gas_price_wei, gas_price); + assert_eq!(result.prev_nonce, nonce); + }; + assert(result_1, amount_in_wei); + assert(result_2, fetched_balance_from_payable_table_opt_2.unwrap()); + } + #[test] fn retry_tx_template_can_be_created_from_failed_tx() { let receiver_address = make_address(42); From 0e53e0439df5d913d313d2221b69167ac846b074 Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 8 Oct 2025 16:28:47 +0200 Subject: [PATCH 02/20] GH-598: second QA fix: added logging --- node/src/accountant/mod.rs | 13 +++-- node/src/accountant/scanners/mod.rs | 1 + .../scanners/payable_scanner/mod.rs | 1 + .../scanners/payable_scanner/start_scan.rs | 2 +- .../tx_templates/initial/retry.rs | 16 ++++-- .../scanners/pending_payable_scanner/mod.rs | 51 +++++++++++++------ .../tx_receipt_interpreter.rs | 7 +-- .../scanners/pending_payable_scanner/utils.rs | 32 ++++++------ .../accountant/scanners/scan_schedulers.rs | 5 +- .../blockchain_interface_web3/utils.rs | 14 ++++- 10 files changed, 93 insertions(+), 49 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 153158dd4..17eee2223 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -348,8 +348,8 @@ impl Handler for Accountant { if let Some(node_to_ui_msg) = ui_msg_opt { info!( self.logger, - "Re-running the pending payable scan is recommended, as some \ - parts did not finish last time." + "Re-running the pending payable scan is recommended, as some parts \ + did not finish last time." ); self.ui_message_sub_opt .as_ref() @@ -417,11 +417,14 @@ impl Handler for Accountant { fn handle(&mut self, scan_error: ScanError, ctx: &mut Self::Context) -> Self::Result { error!(self.logger, "Received ScanError: {:?}", scan_error); + self.scanners .acknowledge_scan_error(&scan_error, &self.logger); match scan_error.response_skeleton_opt { - None => match scan_error.scan_type { + None => { + debug!(self.logger, "Trying to restore continuity after a scan crash"); + match scan_error.scan_type { DetailedScanType::NewPayables => self .scan_schedulers .payable @@ -437,7 +440,7 @@ impl Handler for Accountant { DetailedScanType::Receivables => { self.scan_schedulers.receivable.schedule(ctx, &self.logger) } - }, + }}, Some(response_skeleton) => { let error_msg = NodeToUiMessage { target: ClientId(response_skeleton.client_id), @@ -975,7 +978,7 @@ impl Accountant { None => Err(StartScanError::NoConsumingWalletFound), }; - self.scan_schedulers.payable.reset_scan_timer(); + self.scan_schedulers.payable.reset_scan_timer(&self.logger); match result { Ok(scan_message) => { diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index 787b9a8b2..a2da664ce 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -256,6 +256,7 @@ impl Scanners { } pub fn acknowledge_scan_error(&mut self, error: &ScanError, logger: &Logger) { + debug!(logger, "Acknowledging a scan that couldn't finish"); match error.scan_type { DetailedScanType::NewPayables | DetailedScanType::RetryPayables => { self.payable.mark_as_ended(logger) diff --git a/node/src/accountant/scanners/payable_scanner/mod.rs b/node/src/accountant/scanners/payable_scanner/mod.rs index b82d2c46e..2d8b7cb8b 100644 --- a/node/src/accountant/scanners/payable_scanner/mod.rs +++ b/node/src/accountant/scanners/payable_scanner/mod.rs @@ -251,6 +251,7 @@ impl PayableScanner { self.insert_records_in_sent_payables(&batch_results.sent_txs); } if failed > 0 { + debug!(logger, "Recording failed txs: {:?}", batch_results.failed_txs); self.insert_records_in_failed_payables(&batch_results.failed_txs); } } diff --git a/node/src/accountant/scanners/payable_scanner/start_scan.rs b/node/src/accountant/scanners/payable_scanner/start_scan.rs index 9eda6b359..457ab73ee 100644 --- a/node/src/accountant/scanners/payable_scanner/start_scan.rs +++ b/node/src/accountant/scanners/payable_scanner/start_scan.rs @@ -66,7 +66,7 @@ impl StartableScanner for Payable info!(logger, "Scanning for retry payables"); let failed_txs = self.get_txs_to_retry(); let amount_from_payables = self.find_amount_from_payables(&failed_txs); - let retry_tx_templates = RetryTxTemplates::new(&failed_txs, &amount_from_payables); + let retry_tx_templates = RetryTxTemplates::new(&failed_txs, &amount_from_payables, logger); Ok(InitialTemplatesMessage { initial_templates: Either::Right(retry_tx_templates), diff --git a/node/src/accountant/scanners/payable_scanner/tx_templates/initial/retry.rs b/node/src/accountant/scanners/payable_scanner/tx_templates/initial/retry.rs index 2e209c8d0..6b4cc9707 100644 --- a/node/src/accountant/scanners/payable_scanner/tx_templates/initial/retry.rs +++ b/node/src/accountant/scanners/payable_scanner/tx_templates/initial/retry.rs @@ -4,6 +4,7 @@ use crate::accountant::scanners::payable_scanner::tx_templates::BaseTxTemplate; use std::collections::{BTreeSet, HashMap}; use std::ops::{Deref, DerefMut}; use web3::types::Address; +use masq_lib::logger::Logger; #[derive(Debug, Clone, PartialEq, Eq)] pub struct RetryTxTemplate { @@ -13,10 +14,14 @@ pub struct RetryTxTemplate { } impl RetryTxTemplate { - pub fn new(failed_tx: &FailedTx, updated_payable_balance_opt: Option) -> Self { + pub fn new(failed_tx: &FailedTx, updated_payable_balance_opt: Option, logger: &Logger) -> Self { let mut retry_template = RetryTxTemplate::from(failed_tx); + debug!(logger, "Tx to retry {:?}", failed_tx); + if let Some(updated_payable_balance) = updated_payable_balance_opt { + debug!(logger, "Updating the pay for {:?} from former {} to latest accounted balance {} of minor", failed_tx.receiver_address, failed_tx.amount_minor, updated_payable_balance); + retry_template.base.amount_in_wei = updated_payable_balance; } @@ -44,6 +49,7 @@ impl RetryTxTemplates { pub fn new( txs_to_retry: &BTreeSet, amounts_from_payables: &HashMap, + logger: &Logger, ) -> Self { Self( txs_to_retry @@ -52,7 +58,7 @@ impl RetryTxTemplates { let payable_scan_amount_opt = amounts_from_payables .get(&tx_to_retry.receiver_address) .copied(); - RetryTxTemplate::new(tx_to_retry, payable_scan_amount_opt) + RetryTxTemplate::new(tx_to_retry, payable_scan_amount_opt, logger) }) .collect(), ) @@ -90,6 +96,7 @@ impl IntoIterator for RetryTxTemplates { #[cfg(test)] mod tests { + use masq_lib::logger::Logger; use crate::accountant::db_access_objects::failed_payable_dao::{ FailedTx, FailureReason, FailureStatus, }; @@ -116,11 +123,12 @@ mod tests { reason: FailureReason::PendingTooLong, status: FailureStatus::RetryRequired, }; + let logger = Logger::new("test"); let fetched_balance_from_payable_table_opt_1 = None; let fetched_balance_from_payable_table_opt_2 = Some(1_234_567); - let result_1 = RetryTxTemplate::new(&failed_tx, fetched_balance_from_payable_table_opt_1); - let result_2 = RetryTxTemplate::new(&failed_tx, fetched_balance_from_payable_table_opt_2); + let result_1 = RetryTxTemplate::new(&failed_tx, fetched_balance_from_payable_table_opt_1, &logger); + let result_2 = RetryTxTemplate::new(&failed_tx, fetched_balance_from_payable_table_opt_2, &logger); let assert = |result: RetryTxTemplate, expected_amount_in_wei: u128| { assert_eq!(result.base.receiver_address, receiver_address); diff --git a/node/src/accountant/scanners/pending_payable_scanner/mod.rs b/node/src/accountant/scanners/pending_payable_scanner/mod.rs index 499ee0179..d5bb5cde7 100644 --- a/node/src/accountant/scanners/pending_payable_scanner/mod.rs +++ b/node/src/accountant/scanners/pending_payable_scanner/mod.rs @@ -119,6 +119,8 @@ impl Scanner for PendingPayableScan let retry_opt = scan_report.requires_payments_retry(); + debug!(logger, "Payment retry requirement: {:?}", retry_opt); + self.process_txs_by_state(scan_report, logger); self.mark_as_ended(logger); @@ -161,6 +163,8 @@ impl PendingPayableScanner { } fn harvest_tables(&mut self, logger: &Logger) -> Result, StartScanError> { + debug!(logger,"Harvesting sent_payable and failed_payable tables"); + let pending_tx_hashes_opt = self.harvest_pending_payables(); let failure_hashes_opt = self.harvest_unproven_failures(); @@ -250,8 +254,8 @@ impl PendingPayableScanner { fn emptiness_check(&self, msg: &TxReceiptsMessage) { if msg.results.is_empty() { panic!( - "We should never receive an empty list of results. \ - Even receipts that could not be retrieved can be interpreted" + "We should never receive an empty list of results. Even receipts that could \ + not be retrieved can be interpreted" ) } } @@ -396,14 +400,18 @@ impl PendingPayableScanner { logger: &Logger, ) { self.handle_tx_failure_reclaims(confirmed_txs.reclaims, logger); - self.handle_normal_confirmations(confirmed_txs.normal_confirmations, logger); + self.handle_standard_confirmations(confirmed_txs.standard_confirmations, logger); } fn handle_tx_failure_reclaims(&mut self, reclaimed: Vec, logger: &Logger) { if reclaimed.is_empty() { + debug!(logger, "No failure reclaim to process"); + return; } + debug!(logger, "Processing failure reclaims: {:?}", reclaimed); + let hashes_and_blocks = Self::collect_and_sort_hashes_and_blocks(&reclaimed); self.replace_sent_tx_records(&reclaimed, &hashes_and_blocks, logger); @@ -495,11 +503,15 @@ impl PendingPayableScanner { } } - fn handle_normal_confirmations(&mut self, confirmed_txs: Vec, logger: &Logger) { + fn handle_standard_confirmations(&mut self, confirmed_txs: Vec, logger: &Logger) { if confirmed_txs.is_empty() { + debug!(logger, "No standard tx confirmations to process"); return; } + debug!(logger, "Processing {} standard tx confirmations", confirmed_txs.len()); + trace!(logger, "{:?}", confirmed_txs); + self.confirm_transactions(&confirmed_txs); self.update_tx_blocks(&confirmed_txs, logger); @@ -632,9 +644,12 @@ impl PendingPayableScanner { } if new_failures.is_empty() { + debug!(logger, "No reverted txs to process"); return; } + debug!(logger, "Processing reverted txs {:?}", new_failures); + let new_failures_btree_set: BTreeSet = new_failures.iter().cloned().collect(); if let Err(e) = self @@ -674,9 +689,13 @@ impl PendingPayableScanner { } if rechecks_completed.is_empty() { + debug!(logger, "No recheck-requiring failures to finalize"); return; } + debug!(logger, "Finalizing {} double-checked failures", rechecks_completed.len()); + trace!(logger, "{:?}", rechecks_completed); + match self .failed_payable_dao .update_statuses(&prepare_hashmap(&rechecks_completed)) @@ -830,7 +849,7 @@ impl PendingPayableScanner { debug!( logger, - "Found {} pending payables and {} unfinalized failures to process", + "Found {} pending payables and {} unproven failures to process", resolve_optional_vec(pending_tx_hashes_opt), resolve_optional_vec(failure_hashes_opt) ); @@ -1720,7 +1739,7 @@ mod tests { subject.handle_confirmed_transactions( DetectedConfirmations { - normal_confirmations: vec![], + standard_confirmations: vec![], reclaims: vec![sent_tx_1.clone(), sent_tx_2.clone()], }, &logger, @@ -1785,7 +1804,7 @@ mod tests { subject.handle_confirmed_transactions( DetectedConfirmations { - normal_confirmations: vec![], + standard_confirmations: vec![], reclaims: vec![sent_tx_1.clone(), sent_tx_2.clone()], }, &Logger::new("test"), @@ -1832,7 +1851,7 @@ mod tests { subject.handle_confirmed_transactions( DetectedConfirmations { - normal_confirmations: vec![], + standard_confirmations: vec![], reclaims: vec![sent_tx_1.clone(), sent_tx_2.clone()], }, &Logger::new("test"), @@ -1854,7 +1873,7 @@ mod tests { subject.handle_confirmed_transactions( DetectedConfirmations { - normal_confirmations: vec![], + standard_confirmations: vec![], reclaims: vec![sent_tx.clone()], }, &Logger::new("test"), @@ -1862,9 +1881,9 @@ mod tests { } #[test] - fn handles_normal_confirmations_alone() { + fn handles_standard_confirmations_alone() { init_test_logging(); - let test_name = "handles_normal_confirmations_alone"; + let test_name = "handles_standard_confirmations_alone"; let transactions_confirmed_params_arc = Arc::new(Mutex::new(vec![])); let confirm_tx_params_arc = Arc::new(Mutex::new(vec![])); let payable_dao = PayableDaoMock::default() @@ -1905,7 +1924,7 @@ mod tests { subject.handle_confirmed_transactions( DetectedConfirmations { - normal_confirmations: vec![sent_tx_1.clone(), sent_tx_2.clone()], + standard_confirmations: vec![sent_tx_1.clone(), sent_tx_2.clone()], reclaims: vec![], }, &logger, @@ -1981,7 +2000,7 @@ mod tests { subject.handle_confirmed_transactions( DetectedConfirmations { - normal_confirmations: vec![sent_tx_1.clone()], + standard_confirmations: vec![sent_tx_1.clone()], reclaims: vec![sent_tx_2.clone()], }, &logger, @@ -2045,7 +2064,7 @@ mod tests { subject.handle_confirmed_transactions( DetectedConfirmations { - normal_confirmations: vec![sent_tx_1, sent_tx_2], + standard_confirmations: vec![sent_tx_1, sent_tx_2], reclaims: vec![], }, &Logger::new("test"), @@ -2071,7 +2090,7 @@ mod tests { subject.handle_confirmed_transactions( DetectedConfirmations { - normal_confirmations: vec![sent_tx], + standard_confirmations: vec![sent_tx], reclaims: vec![], }, &Logger::new("test"), @@ -2153,7 +2172,7 @@ mod tests { subject.handle_confirmed_transactions( DetectedConfirmations { - normal_confirmations: vec![sent_tx_1, sent_tx_2], + standard_confirmations: vec![sent_tx_1, sent_tx_2], reclaims: vec![sent_tx_3], }, &Logger::new(test_name), diff --git a/node/src/accountant/scanners/pending_payable_scanner/tx_receipt_interpreter.rs b/node/src/accountant/scanners/pending_payable_scanner/tx_receipt_interpreter.rs index fc16c5713..5f2703cb2 100644 --- a/node/src/accountant/scanners/pending_payable_scanner/tx_receipt_interpreter.rs +++ b/node/src/accountant/scanners/pending_payable_scanner/tx_receipt_interpreter.rs @@ -31,6 +31,7 @@ impl TxReceiptInterpreter { pending_payable_scanner: &PendingPayableScanner, logger: &Logger, ) -> ReceiptScanReport { + debug!(logger, "Composing receipt scan report"); let scan_report = ReceiptScanReport::default(); tx_cases .into_iter() @@ -162,7 +163,7 @@ impl TxReceiptInterpreter { scan_report } - //TODO: failures handling might need enhancement suggested by GH-693 + //TODO: if wanted, address GH-693 for more detailed failures fn handle_reverted_tx( mut scan_report: ReceiptScanReport, tx: TxByTable, @@ -279,7 +280,7 @@ mod tests { ReceiptScanReport { failures: DetectedFailures::default(), confirmations: DetectedConfirmations { - normal_confirmations: vec![updated_tx], + standard_confirmations: vec![updated_tx], reclaims: vec![] } } @@ -327,7 +328,7 @@ mod tests { ReceiptScanReport { failures: DetectedFailures::default(), confirmations: DetectedConfirmations { - normal_confirmations: vec![], + standard_confirmations: vec![], reclaims: vec![sent_tx] } } diff --git a/node/src/accountant/scanners/pending_payable_scanner/utils.rs b/node/src/accountant/scanners/pending_payable_scanner/utils.rs index 7a1d18eaa..2ea92e289 100644 --- a/node/src/accountant/scanners/pending_payable_scanner/utils.rs +++ b/node/src/accountant/scanners/pending_payable_scanner/utils.rs @@ -38,7 +38,7 @@ impl ReceiptScanReport { confirmation_type: ConfirmationType, ) { match confirmation_type { - ConfirmationType::Normal => self.confirmations.normal_confirmations.push(confirmed_tx), + ConfirmationType::Normal => self.confirmations.standard_confirmations.push(confirmed_tx), ConfirmationType::Reclaim => self.confirmations.reclaims.push(confirmed_tx), } } @@ -62,13 +62,13 @@ impl ReceiptScanReport { #[derive(Debug, Default, PartialEq, Eq, Clone)] pub struct DetectedConfirmations { - pub normal_confirmations: Vec, + pub standard_confirmations: Vec, pub reclaims: Vec, } impl DetectedConfirmations { pub(super) fn is_empty(&self) -> bool { - self.normal_confirmations.is_empty() && self.reclaims.is_empty() + self.standard_confirmations.is_empty() && self.reclaims.is_empty() } } @@ -410,7 +410,7 @@ mod tests { #[test] fn detected_confirmations_is_empty_works() { let subject = DetectedConfirmations { - normal_confirmations: vec![], + standard_confirmations: vec![], reclaims: vec![], }; @@ -462,19 +462,19 @@ mod tests { ]; let detected_confirmations_feeding = vec![ DetectedConfirmations { - normal_confirmations: vec![], + standard_confirmations: vec![], reclaims: vec![], }, DetectedConfirmations { - normal_confirmations: vec![make_sent_tx(456)], + standard_confirmations: vec![make_sent_tx(456)], reclaims: vec![make_sent_tx(999)], }, DetectedConfirmations { - normal_confirmations: vec![make_sent_tx(777)], + standard_confirmations: vec![make_sent_tx(777)], reclaims: vec![], }, DetectedConfirmations { - normal_confirmations: vec![], + standard_confirmations: vec![], reclaims: vec![make_sent_tx(999)], }, ]; @@ -550,19 +550,19 @@ mod tests { ]; let detected_confirmations_feeding = vec![ DetectedConfirmations { - normal_confirmations: vec![], + standard_confirmations: vec![], reclaims: vec![], }, DetectedConfirmations { - normal_confirmations: vec![make_sent_tx(777)], + standard_confirmations: vec![make_sent_tx(777)], reclaims: vec![make_sent_tx(999)], }, DetectedConfirmations { - normal_confirmations: vec![make_sent_tx(777)], + standard_confirmations: vec![make_sent_tx(777)], reclaims: vec![], }, DetectedConfirmations { - normal_confirmations: vec![], + standard_confirmations: vec![], reclaims: vec![make_sent_tx(999)], }, ]; @@ -594,15 +594,15 @@ mod tests { fn requires_payments_retry_says_no() { let detected_confirmations_feeding = vec![ DetectedConfirmations { - normal_confirmations: vec![make_sent_tx(777)], + standard_confirmations: vec![make_sent_tx(777)], reclaims: vec![make_sent_tx(999)], }, DetectedConfirmations { - normal_confirmations: vec![make_sent_tx(777)], + standard_confirmations: vec![make_sent_tx(777)], reclaims: vec![], }, DetectedConfirmations { - normal_confirmations: vec![], + standard_confirmations: vec![], reclaims: vec![make_sent_tx(999)], }, ]; @@ -638,7 +638,7 @@ mod tests { tx_receipt_rpc_failures: vec![], }, confirmations: DetectedConfirmations { - normal_confirmations: vec![], + standard_confirmations: vec![], reclaims: vec![], }, }; diff --git a/node/src/accountant/scanners/scan_schedulers.rs b/node/src/accountant/scanners/scan_schedulers.rs index dedfee1e5..54a33dd2b 100644 --- a/node/src/accountant/scanners/scan_schedulers.rs +++ b/node/src/accountant/scanners/scan_schedulers.rs @@ -122,7 +122,8 @@ impl PayableScanScheduler { } } - pub fn reset_scan_timer(&mut self) { + pub fn reset_scan_timer(&mut self, logger: &Logger) { + debug!(logger, "NewPayableScanIntervalComputer timer reset"); self.interval_computer.reset_last_scan_timestamp(); } @@ -612,7 +613,7 @@ mod tests { .reset_last_scan_timestamp_params(&reset_last_scan_timestamp_params_arc), ); - subject.payable.reset_scan_timer(); + subject.payable.reset_scan_timer(&Logger::new("test")); let reset_last_scan_timestamp_params = reset_last_scan_timestamp_params_arc.lock().unwrap(); assert_eq!(*reset_last_scan_timestamp_params, vec![()]) diff --git a/node/src/blockchain/blockchain_interface/blockchain_interface_web3/utils.rs b/node/src/blockchain/blockchain_interface/blockchain_interface_web3/utils.rs index 16cab8c2d..4f2d91fe8 100644 --- a/node/src/blockchain/blockchain_interface/blockchain_interface_web3/utils.rs +++ b/node/src/blockchain/blockchain_interface/blockchain_interface_web3/utils.rs @@ -282,11 +282,16 @@ pub fn send_payables_within_batch( transmission_log(chain, &signable_tx_templates) ); + let logger_clone = logger.clone(); + Box::new( web3_batch .transport() .submit_batch() - .map_err(move |e| return_sending_error(&sent_txs_for_err, &e)) + .map_err(move |e| { + warning!(logger_clone, "Failed to submit batch to Web3 client: {}", e); + return_sending_error(&sent_txs_for_err, &e) + }) .and_then(move |batch_responses| Ok(return_batch_results(sent_txs, batch_responses))), ) } @@ -727,6 +732,7 @@ mod tests { #[test] fn send_payables_within_batch_fails_on_submit_batch_call() { + let test_name = "send_payables_within_batch_fails_on_submit_batch_call"; let port = find_free_port(); let (_event_loop_handle, transport) = Http::with_max_parallel( &format!("http://{}:{}", &Ipv4Addr::LOCALHOST.to_string(), port), @@ -780,11 +786,15 @@ mod tests { }); test_send_payables_within_batch( - "send_payables_within_batch_fails_on_submit_batch_call", + test_name, signable_tx_templates, expected_result, port, ); + + TestLogHandler::new().exists_log_containing(&format!("WARN: {test_name}: \ + Failed to submit batch to Web3 client: Transport error: Error(Connect, Os {{ code: 111, \ + kind: ConnectionRefused, message: \"Connection refused\" }}")); } #[test] From bc95399f27b1cd811b1ea3a5d711314d458d441b Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 8 Oct 2025 16:33:12 +0200 Subject: [PATCH 03/20] GH-598: second QA fix: formatting --- node/src/accountant/mod.rs | 40 ++++++++++--------- .../scanners/payable_scanner/mod.rs | 5 ++- .../tx_templates/initial/retry.rs | 30 +++++++++++--- .../scanners/pending_payable_scanner/mod.rs | 18 ++++++--- .../scanners/pending_payable_scanner/utils.rs | 4 +- .../blockchain_interface_web3/utils.rs | 13 +++--- 6 files changed, 71 insertions(+), 39 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 17eee2223..04d485663 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -417,30 +417,34 @@ impl Handler for Accountant { fn handle(&mut self, scan_error: ScanError, ctx: &mut Self::Context) -> Self::Result { error!(self.logger, "Received ScanError: {:?}", scan_error); - + self.scanners .acknowledge_scan_error(&scan_error, &self.logger); match scan_error.response_skeleton_opt { - None => { - debug!(self.logger, "Trying to restore continuity after a scan crash"); + None => { + debug!( + self.logger, + "Trying to restore the scan train after a crash" + ); match scan_error.scan_type { - DetailedScanType::NewPayables => self - .scan_schedulers - .payable - .schedule_new_payable_scan(ctx, &self.logger), - DetailedScanType::RetryPayables => self - .scan_schedulers - .payable - .schedule_retry_payable_scan(ctx, None, &self.logger), - DetailedScanType::PendingPayables => self - .scan_schedulers - .pending_payable - .schedule(ctx, &self.logger), - DetailedScanType::Receivables => { - self.scan_schedulers.receivable.schedule(ctx, &self.logger) + DetailedScanType::NewPayables => self + .scan_schedulers + .payable + .schedule_new_payable_scan(ctx, &self.logger), + DetailedScanType::RetryPayables => self + .scan_schedulers + .payable + .schedule_retry_payable_scan(ctx, None, &self.logger), + DetailedScanType::PendingPayables => self + .scan_schedulers + .pending_payable + .schedule(ctx, &self.logger), + DetailedScanType::Receivables => { + self.scan_schedulers.receivable.schedule(ctx, &self.logger) + } } - }}, + } Some(response_skeleton) => { let error_msg = NodeToUiMessage { target: ClientId(response_skeleton.client_id), diff --git a/node/src/accountant/scanners/payable_scanner/mod.rs b/node/src/accountant/scanners/payable_scanner/mod.rs index 2d8b7cb8b..4c9ac9804 100644 --- a/node/src/accountant/scanners/payable_scanner/mod.rs +++ b/node/src/accountant/scanners/payable_scanner/mod.rs @@ -251,7 +251,10 @@ impl PayableScanner { self.insert_records_in_sent_payables(&batch_results.sent_txs); } if failed > 0 { - debug!(logger, "Recording failed txs: {:?}", batch_results.failed_txs); + debug!( + logger, + "Recording failed txs: {:?}", batch_results.failed_txs + ); self.insert_records_in_failed_payables(&batch_results.failed_txs); } } diff --git a/node/src/accountant/scanners/payable_scanner/tx_templates/initial/retry.rs b/node/src/accountant/scanners/payable_scanner/tx_templates/initial/retry.rs index 6b4cc9707..8157a373b 100644 --- a/node/src/accountant/scanners/payable_scanner/tx_templates/initial/retry.rs +++ b/node/src/accountant/scanners/payable_scanner/tx_templates/initial/retry.rs @@ -1,10 +1,10 @@ // Copyright (c) 2025, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use crate::accountant::db_access_objects::failed_payable_dao::FailedTx; use crate::accountant::scanners::payable_scanner::tx_templates::BaseTxTemplate; +use masq_lib::logger::Logger; use std::collections::{BTreeSet, HashMap}; use std::ops::{Deref, DerefMut}; use web3::types::Address; -use masq_lib::logger::Logger; #[derive(Debug, Clone, PartialEq, Eq)] pub struct RetryTxTemplate { @@ -14,13 +14,23 @@ pub struct RetryTxTemplate { } impl RetryTxTemplate { - pub fn new(failed_tx: &FailedTx, updated_payable_balance_opt: Option, logger: &Logger) -> Self { + pub fn new( + failed_tx: &FailedTx, + updated_payable_balance_opt: Option, + logger: &Logger, + ) -> Self { let mut retry_template = RetryTxTemplate::from(failed_tx); debug!(logger, "Tx to retry {:?}", failed_tx); if let Some(updated_payable_balance) = updated_payable_balance_opt { - debug!(logger, "Updating the pay for {:?} from former {} to latest accounted balance {} of minor", failed_tx.receiver_address, failed_tx.amount_minor, updated_payable_balance); + debug!( + logger, + "Updating the pay for {:?} from former {} to latest accounted balance {} of minor", + failed_tx.receiver_address, + failed_tx.amount_minor, + updated_payable_balance + ); retry_template.base.amount_in_wei = updated_payable_balance; } @@ -96,7 +106,6 @@ impl IntoIterator for RetryTxTemplates { #[cfg(test)] mod tests { - use masq_lib::logger::Logger; use crate::accountant::db_access_objects::failed_payable_dao::{ FailedTx, FailureReason, FailureStatus, }; @@ -105,6 +114,7 @@ mod tests { }; use crate::accountant::scanners::payable_scanner::tx_templates::BaseTxTemplate; use crate::blockchain::test_utils::{make_address, make_tx_hash}; + use masq_lib::logger::Logger; #[test] fn retry_tx_template_constructor_works() { @@ -127,8 +137,16 @@ mod tests { let fetched_balance_from_payable_table_opt_1 = None; let fetched_balance_from_payable_table_opt_2 = Some(1_234_567); - let result_1 = RetryTxTemplate::new(&failed_tx, fetched_balance_from_payable_table_opt_1, &logger); - let result_2 = RetryTxTemplate::new(&failed_tx, fetched_balance_from_payable_table_opt_2, &logger); + let result_1 = RetryTxTemplate::new( + &failed_tx, + fetched_balance_from_payable_table_opt_1, + &logger, + ); + let result_2 = RetryTxTemplate::new( + &failed_tx, + fetched_balance_from_payable_table_opt_2, + &logger, + ); let assert = |result: RetryTxTemplate, expected_amount_in_wei: u128| { assert_eq!(result.base.receiver_address, receiver_address); diff --git a/node/src/accountant/scanners/pending_payable_scanner/mod.rs b/node/src/accountant/scanners/pending_payable_scanner/mod.rs index d5bb5cde7..d8faa57fa 100644 --- a/node/src/accountant/scanners/pending_payable_scanner/mod.rs +++ b/node/src/accountant/scanners/pending_payable_scanner/mod.rs @@ -163,7 +163,7 @@ impl PendingPayableScanner { } fn harvest_tables(&mut self, logger: &Logger) -> Result, StartScanError> { - debug!(logger,"Harvesting sent_payable and failed_payable tables"); + debug!(logger, "Harvesting sent_payable and failed_payable tables"); let pending_tx_hashes_opt = self.harvest_pending_payables(); let failure_hashes_opt = self.harvest_unproven_failures(); @@ -509,7 +509,11 @@ impl PendingPayableScanner { return; } - debug!(logger, "Processing {} standard tx confirmations", confirmed_txs.len()); + debug!( + logger, + "Processing {} standard tx confirmations", + confirmed_txs.len() + ); trace!(logger, "{:?}", confirmed_txs); self.confirm_transactions(&confirmed_txs); @@ -649,7 +653,7 @@ impl PendingPayableScanner { } debug!(logger, "Processing reverted txs {:?}", new_failures); - + let new_failures_btree_set: BTreeSet = new_failures.iter().cloned().collect(); if let Err(e) = self @@ -693,9 +697,13 @@ impl PendingPayableScanner { return; } - debug!(logger, "Finalizing {} double-checked failures", rechecks_completed.len()); + debug!( + logger, + "Finalizing {} double-checked failures", + rechecks_completed.len() + ); trace!(logger, "{:?}", rechecks_completed); - + match self .failed_payable_dao .update_statuses(&prepare_hashmap(&rechecks_completed)) diff --git a/node/src/accountant/scanners/pending_payable_scanner/utils.rs b/node/src/accountant/scanners/pending_payable_scanner/utils.rs index 2ea92e289..2a1c5c7cb 100644 --- a/node/src/accountant/scanners/pending_payable_scanner/utils.rs +++ b/node/src/accountant/scanners/pending_payable_scanner/utils.rs @@ -38,7 +38,9 @@ impl ReceiptScanReport { confirmation_type: ConfirmationType, ) { match confirmation_type { - ConfirmationType::Normal => self.confirmations.standard_confirmations.push(confirmed_tx), + ConfirmationType::Normal => { + self.confirmations.standard_confirmations.push(confirmed_tx) + } ConfirmationType::Reclaim => self.confirmations.reclaims.push(confirmed_tx), } } diff --git a/node/src/blockchain/blockchain_interface/blockchain_interface_web3/utils.rs b/node/src/blockchain/blockchain_interface/blockchain_interface_web3/utils.rs index 4f2d91fe8..00aeeee03 100644 --- a/node/src/blockchain/blockchain_interface/blockchain_interface_web3/utils.rs +++ b/node/src/blockchain/blockchain_interface/blockchain_interface_web3/utils.rs @@ -785,16 +785,13 @@ mod tests { failed_txs, }); - test_send_payables_within_batch( - test_name, - signable_tx_templates, - expected_result, - port, - ); + test_send_payables_within_batch(test_name, signable_tx_templates, expected_result, port); - TestLogHandler::new().exists_log_containing(&format!("WARN: {test_name}: \ + TestLogHandler::new().exists_log_containing(&format!( + "WARN: {test_name}: \ Failed to submit batch to Web3 client: Transport error: Error(Connect, Os {{ code: 111, \ - kind: ConnectionRefused, message: \"Connection refused\" }}")); + kind: ConnectionRefused, message: \"Connection refused\" }}" + )); } #[test] From b9f6c648e767022a2b1fb6d09e3b132abe302f9c Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 8 Oct 2025 17:06:33 +0200 Subject: [PATCH 04/20] GH-598: second QA fix: finished all.sh --- node/src/accountant/scanners/mod.rs | 8 ++-- .../scanners/pending_payable_scanner/mod.rs | 41 +++++++++---------- .../tx_receipt_interpreter.rs | 2 +- .../scanners/pending_payable_scanner/utils.rs | 2 +- node/src/accountant/test_utils.rs | 8 ++-- 5 files changed, 30 insertions(+), 31 deletions(-) diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index a2da664ce..cbc337a8d 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -795,11 +795,11 @@ mod tests { false ); let dumped_records = pending_payable_scanner - .yet_unproven_failed_payables + .suspected_failed_payables .dump_cache(); assert!( dumped_records.is_empty(), - "There should be no yet unproven failures but found {:?}.", + "There should be no suspected failures but found {:?}.", dumped_records ); assert_eq!( @@ -1200,7 +1200,9 @@ mod tests { ); TestLogHandler::new().assert_logs_match_in_order(vec![ &format!("INFO: {test_name}: Scanning for pending payable"), - &format!("DEBUG: {test_name}: Found 1 pending payables and 1 unfinalized failures to process"), + &format!( + "DEBUG: {test_name}: Found 1 pending payables and 1 suspected failures to process" + ), ]) } diff --git a/node/src/accountant/scanners/pending_payable_scanner/mod.rs b/node/src/accountant/scanners/pending_payable_scanner/mod.rs index d8faa57fa..4fe12add1 100644 --- a/node/src/accountant/scanners/pending_payable_scanner/mod.rs +++ b/node/src/accountant/scanners/pending_payable_scanner/mod.rs @@ -65,7 +65,7 @@ pub struct PendingPayableScanner { pub failed_payable_dao: Box, pub financial_statistics: Rc>, pub current_sent_payables: Box>, - pub yet_unproven_failed_payables: Box>, + pub suspected_failed_payables: Box>, pub clock: Box, } @@ -138,7 +138,7 @@ impl Scanner for PendingPayableScan impl CachesEmptiableScanner for PendingPayableScanner { fn empty_caches(&mut self, logger: &Logger) { self.current_sent_payables.ensure_empty_cache(logger); - self.yet_unproven_failed_payables.ensure_empty_cache(logger); + self.suspected_failed_payables.ensure_empty_cache(logger); } } @@ -157,7 +157,7 @@ impl PendingPayableScanner { failed_payable_dao, financial_statistics, current_sent_payables: Box::new(CurrentPendingPayables::default()), - yet_unproven_failed_payables: Box::new(RecheckRequiringFailures::default()), + suspected_failed_payables: Box::new(RecheckRequiringFailures::default()), clock: Box::new(SimpleClockReal::default()), } } @@ -166,7 +166,7 @@ impl PendingPayableScanner { debug!(logger, "Harvesting sent_payable and failed_payable tables"); let pending_tx_hashes_opt = self.harvest_pending_payables(); - let failure_hashes_opt = self.harvest_unproven_failures(); + let failure_hashes_opt = self.harvest_suspected_failures(); if Self::is_there_nothing_to_process( pending_tx_hashes_opt.as_ref(), @@ -203,7 +203,7 @@ impl PendingPayableScanner { Some(pending_tx_hashes) } - fn harvest_unproven_failures(&mut self) -> Option> { + fn harvest_suspected_failures(&mut self) -> Option> { let failures = self .failed_payable_dao .retrieve_txs(Some(FailureRetrieveCondition::EveryRecheckRequiredRecord)) @@ -215,7 +215,7 @@ impl PendingPayableScanner { } let failure_hashes = Self::wrap_hashes(&failures, TxHashByTable::FailedPayable); - self.yet_unproven_failed_payables.load_cache(failures); + self.suspected_failed_payables.load_cache(failures); Some(failure_hashes) } @@ -329,7 +329,7 @@ impl PendingPayableScanner { }; self.current_sent_payables.ensure_empty_cache(logger); - self.yet_unproven_failed_payables.ensure_empty_cache(logger); + self.suspected_failed_payables.ensure_empty_cache(logger); cases } @@ -354,10 +354,7 @@ impl PendingPayableScanner { } } TxHashByTable::FailedPayable(tx_hash) => { - match self - .yet_unproven_failed_payables - .get_record_by_hash(tx_hash) - { + match self.suspected_failed_payables.get_record_by_hash(tx_hash) { Some(failed_tx) => { cases.push(TxCaseToBeInterpreted::new( TxByTable::FailedPayable(failed_tx), @@ -382,10 +379,10 @@ impl PendingPayableScanner { panic!( "Looking up '{:?}' in the cache, the record could not be found. Dumping \ - the remaining values. Pending payables: {:?}. Unproven failures: {:?}.", + the remaining values. Pending payables: {:?}. Suspected failures: {:?}.", missing_entry, rearrange(self.current_sent_payables.dump_cache()), - rearrange(self.yet_unproven_failed_payables.dump_cache()), + rearrange(self.suspected_failed_payables.dump_cache()), ) } @@ -632,7 +629,7 @@ impl PendingPayableScanner { }); self.add_new_failures(grouped_failures.new_failures, logger); - self.finalize_unproven_failures(grouped_failures.rechecks_completed, logger); + self.finalize_suspected_failures(grouped_failures.rechecks_completed, logger); } fn add_new_failures(&self, new_failures: Vec, logger: &Logger) { @@ -684,7 +681,7 @@ impl PendingPayableScanner { } } - fn finalize_unproven_failures(&self, rechecks_completed: Vec, logger: &Logger) { + fn finalize_suspected_failures(&self, rechecks_completed: Vec, logger: &Logger) { fn prepare_hashmap(rechecks_completed: &[TxHash]) -> HashMap { rechecks_completed .iter() @@ -857,7 +854,7 @@ impl PendingPayableScanner { debug!( logger, - "Found {} pending payables and {} unproven failures to process", + "Found {} pending payables and {} suspected failures to process", resolve_optional_vec(pending_tx_hashes_opt), resolve_optional_vec(failure_hashes_opt) ); @@ -932,7 +929,7 @@ mod tests { .build(); let logger = Logger::new("start_scan_fills_in_caches_and_returns_msg"); let pending_payable_cache_before = subject.current_sent_payables.dump_cache(); - let failed_payable_cache_before = subject.yet_unproven_failed_payables.dump_cache(); + let failed_payable_cache_before = subject.suspected_failed_payables.dump_cache(); let result = subject.start_scan(&make_wallet("blah"), SystemTime::now(), None, &logger); @@ -959,7 +956,7 @@ mod tests { failed_payable_cache_before ); let pending_payable_cache_after = subject.current_sent_payables.dump_cache(); - let failed_payable_cache_after = subject.yet_unproven_failed_payables.dump_cache(); + let failed_payable_cache_after = subject.suspected_failed_payables.dump_cache(); assert_eq!( pending_payable_cache_after, hashmap!(sent_tx_hash_1 => sent_tx_1, sent_tx_hash_2 => sent_tx_2) @@ -1067,7 +1064,7 @@ mod tests { failed_payable_cache.load_cache(vec![failed_tx_1, failed_tx_2]); let mut subject = PendingPayableScannerBuilder::new().build(); subject.current_sent_payables = Box::new(pending_payable_cache); - subject.yet_unproven_failed_payables = Box::new(failed_payable_cache); + subject.suspected_failed_payables = Box::new(failed_payable_cache); let logger = Logger::new("test"); let msg = TxReceiptsMessage { results: btreemap![TxHashByTable::SentPayable(sent_tx_hash_1) => Ok( @@ -1088,7 +1085,7 @@ mod tests { values. Pending payables: [SentTx { hash: 0x0000000000000000000000000000000000000000000000\ 000000000000000890, receiver_address: 0x0000000000000000000558000000000558000000, \ amount_minor: 43237380096, timestamp: 29942784, gas_price_minor: 94818816, nonce: 456, \ - status: Pending(Waiting) }]. Unproven failures: []."; + status: Pending(Waiting) }]. Suspected failures: []."; assert_eq!(panic_msg, expected); } @@ -1107,7 +1104,7 @@ mod tests { failed_payable_cache.load_cache(vec![failed_tx_1]); let mut subject = PendingPayableScannerBuilder::new().build(); subject.current_sent_payables = Box::new(pending_payable_cache); - subject.yet_unproven_failed_payables = Box::new(failed_payable_cache); + subject.suspected_failed_payables = Box::new(failed_payable_cache); let logger = Logger::new("test"); let msg = TxReceiptsMessage { results: btreemap![TxHashByTable::SentPayable(sent_tx_hash_1) => Ok(StatusReadFromReceiptCheck::Pending), @@ -1130,7 +1127,7 @@ mod tests { Pending(Waiting) }, SentTx { hash: 0x0000000000000000000000000000000000000000000000000000000\ 000000315, receiver_address: 0x000000000000000000093f00000000093f000000, amount_minor: \ 387532395441, timestamp: 89643024, gas_price_minor: 491169069, nonce: 789, status: \ - Pending(Waiting) }]. Unproven failures: []."; + Pending(Waiting) }]. Suspected failures: []."; assert_eq!(panic_msg, expected); } diff --git a/node/src/accountant/scanners/pending_payable_scanner/tx_receipt_interpreter.rs b/node/src/accountant/scanners/pending_payable_scanner/tx_receipt_interpreter.rs index 5f2703cb2..d04d458b4 100644 --- a/node/src/accountant/scanners/pending_payable_scanner/tx_receipt_interpreter.rs +++ b/node/src/accountant/scanners/pending_payable_scanner/tx_receipt_interpreter.rs @@ -186,7 +186,7 @@ impl TxReceiptInterpreter { failed_tx.reason, ); - scan_report.register_finalization_of_unproven_failure(failed_tx.hash); + scan_report.register_finalization_of_suspected_failure(failed_tx.hash); } } scan_report diff --git a/node/src/accountant/scanners/pending_payable_scanner/utils.rs b/node/src/accountant/scanners/pending_payable_scanner/utils.rs index 2a1c5c7cb..2b8052496 100644 --- a/node/src/accountant/scanners/pending_payable_scanner/utils.rs +++ b/node/src/accountant/scanners/pending_payable_scanner/utils.rs @@ -51,7 +51,7 @@ impl ReceiptScanReport { .push(PresortedTxFailure::NewEntry(failed_tx)); } - pub(super) fn register_finalization_of_unproven_failure(&mut self, tx_hash: TxHash) { + pub(super) fn register_finalization_of_suspected_failure(&mut self, tx_hash: TxHash) { self.failures .tx_failures .push(PresortedTxFailure::RecheckCompleted(tx_hash)); diff --git a/node/src/accountant/test_utils.rs b/node/src/accountant/test_utils.rs index b460363ee..9e3e06575 100644 --- a/node/src/accountant/test_utils.rs +++ b/node/src/accountant/test_utils.rs @@ -1281,7 +1281,7 @@ pub struct PendingPayableScannerBuilder { payment_thresholds: PaymentThresholds, financial_statistics: FinancialStatistics, current_sent_payables: Box>, - yet_unproven_failed_payables: Box>, + suspected_failed_payables: Box>, clock: Box, } @@ -1294,7 +1294,7 @@ impl PendingPayableScannerBuilder { payment_thresholds: PaymentThresholds::default(), financial_statistics: FinancialStatistics::default(), current_sent_payables: Box::new(PendingPayableCacheMock::default()), - yet_unproven_failed_payables: Box::new(PendingPayableCacheMock::default()), + suspected_failed_payables: Box::new(PendingPayableCacheMock::default()), clock: Box::new(SimpleClockMock::default()), } } @@ -1323,7 +1323,7 @@ impl PendingPayableScannerBuilder { mut self, failures: Box>, ) -> Self { - self.yet_unproven_failed_payables = failures; + self.suspected_failed_payables = failures; self } @@ -1341,7 +1341,7 @@ impl PendingPayableScannerBuilder { Rc::new(RefCell::new(self.financial_statistics)), ); scanner.current_sent_payables = self.current_sent_payables; - scanner.yet_unproven_failed_payables = self.yet_unproven_failed_payables; + scanner.suspected_failed_payables = self.suspected_failed_payables; scanner.clock = self.clock; scanner } From 1acd12d0c428c68d9b32dc3dd85b1c3d79145bef Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 8 Oct 2025 17:17:44 +0200 Subject: [PATCH 05/20] GH-598: second QA fix: fix according to rev advice --- node/src/accountant/mod.rs | 13 ++++--------- 1 file changed, 4 insertions(+), 9 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 04d485663..798bd5405 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -3340,7 +3340,7 @@ mod tests { Mutex, Logger, String)>>, >, notify_and_notify_later_params: &NotifyAndNotifyLaterParams, - time_until_next_scan_until_next_new_payable_scan_params_arc: Arc>>, + time_until_next_new_payable_scan_params_arc: Arc>>, new_payable_expected_computed_interval: Duration, ) { // Note that there is no functionality from the payable scanner actually running. @@ -3358,14 +3358,9 @@ mod tests { new_payable_expected_computed_interval )] ); - let time_until_next_scan_until_next_new_payable_scan_params = - time_until_next_scan_until_next_new_payable_scan_params_arc - .lock() - .unwrap(); - assert_eq!( - *time_until_next_scan_until_next_new_payable_scan_params, - vec![()] - ); + let time_until_next_new_payable_scan_params = + time_until_next_new_payable_scan_params_arc.lock().unwrap(); + assert_eq!(*time_until_next_new_payable_scan_params, vec![()]); let payable_scanner_start_scan = payable_scanner_start_scan_arc.lock().unwrap(); assert!( payable_scanner_start_scan.is_empty(), From f77eaa97b9bb789176a6e12dc4502610577d0300 Mon Sep 17 00:00:00 2001 From: Bert Date: Thu, 9 Oct 2025 00:04:08 +0200 Subject: [PATCH 06/20] GH-598: fixed test to be OS agnostic --- multinode_integration_tests/tests/verify_bill_payment.rs | 2 +- .../blockchain_interface_web3/utils.rs | 8 +++++--- 2 files changed, 6 insertions(+), 4 deletions(-) diff --git a/multinode_integration_tests/tests/verify_bill_payment.rs b/multinode_integration_tests/tests/verify_bill_payment.rs index d421f82b2..e5fddc67f 100644 --- a/multinode_integration_tests/tests/verify_bill_payment.rs +++ b/multinode_integration_tests/tests/verify_bill_payment.rs @@ -465,7 +465,7 @@ fn verify_pending_payables() { } MASQNodeUtils::assert_node_wrote_log_containing( real_consuming_node.name(), - "Found 3 pending payables and 0 unfinalized failures to process", + "Found 3 pending payables and 0 suspected failures to process", Duration::from_secs(5), ); MASQNodeUtils::assert_node_wrote_log_containing( diff --git a/node/src/blockchain/blockchain_interface/blockchain_interface_web3/utils.rs b/node/src/blockchain/blockchain_interface/blockchain_interface_web3/utils.rs index 00aeeee03..fa893b19f 100644 --- a/node/src/blockchain/blockchain_interface/blockchain_interface_web3/utils.rs +++ b/node/src/blockchain/blockchain_interface/blockchain_interface_web3/utils.rs @@ -787,10 +787,12 @@ mod tests { test_send_payables_within_batch(test_name, signable_tx_templates, expected_result, port); + let os_specific_code = transport_error_code(); + let os_specific_msg = transport_error_message(); TestLogHandler::new().exists_log_containing(&format!( - "WARN: {test_name}: \ - Failed to submit batch to Web3 client: Transport error: Error(Connect, Os {{ code: 111, \ - kind: ConnectionRefused, message: \"Connection refused\" }}" + "WARN: {test_name}: Failed to submit batch to Web3 client: Transport error: \ + Error(Connect, Os {{ code: {}, kind: ConnectionRefused, message: \"{}\" }}", + os_specific_code, os_specific_msg )); } From 738ead8ccf74d29856c6666790db957d4352a5b9 Mon Sep 17 00:00:00 2001 From: utkarshg6 Date: Thu, 16 Oct 2025 12:27:50 +0530 Subject: [PATCH 07/20] GH-598: add 5 mins interval for retry scan --- node/src/accountant/mod.rs | 118 ++++++++++-------- .../accountant/scanners/scan_schedulers.rs | 18 ++- 2 files changed, 82 insertions(+), 54 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 798bd5405..f37a6ea1b 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -1319,6 +1319,7 @@ mod tests { use crate::accountant::scanners::pending_payable_scanner::utils::TxByTable; use crate::accountant::scanners::scan_schedulers::{ NewPayableScanIntervalComputer, NewPayableScanIntervalComputerReal, ScanTiming, + DEFAULT_RETRY_INTERVAL, }; use crate::accountant::scanners::test_utils::{ MarkScanner, NewPayableScanIntervalComputerMock, PendingPayableCacheMock, ReplacementType, @@ -2044,8 +2045,8 @@ mod tests { let system = System::new("test"); subject.scan_schedulers.automatic_scans_enabled = false; // Making sure we would kill the test if any sort of scan was scheduled - subject.scan_schedulers.payable.retry_payable_notify = - Box::new(NotifyHandleMock::default().panic_on_schedule_attempt()); + subject.scan_schedulers.payable.retry_payable_notify_later = + Box::new(NotifyLaterHandleMock::default().panic_on_schedule_attempt()); subject.scan_schedulers.payable.new_payable_notify_later = Box::new(NotifyLaterHandleMock::default().panic_on_schedule_attempt()); subject.scan_schedulers.payable.new_payable_notify = @@ -3062,7 +3063,7 @@ mod tests { struct NotifyAndNotifyLaterParams { new_payables_notify_later: Arc>>, new_payables_notify: Arc>>, - retry_payables_notify: Arc>>, + retry_payables_notify_later: Arc>>, pending_payables_notify_later: Arc>>, receivables_notify_later: Arc>>, } @@ -3102,9 +3103,9 @@ mod tests { NotifyLaterHandleMock::default() .notify_later_params(¬ify_and_notify_later_params.new_payables_notify_later), ); - subject.scan_schedulers.payable.retry_payable_notify = Box::new( - NotifyHandleMock::default() - .notify_params(¬ify_and_notify_later_params.retry_payables_notify), + subject.scan_schedulers.payable.retry_payable_notify_later = Box::new( + NotifyLaterHandleMock::default() + .notify_later_params(¬ify_and_notify_later_params.retry_payables_notify_later), ); subject.scan_schedulers.payable.new_payable_notify = Box::new( NotifyHandleMock::default() @@ -3164,9 +3165,9 @@ mod tests { NotifyLaterHandleMock::default() .notify_later_params(¬ify_and_notify_later_params.new_payables_notify_later), ); - subject.scan_schedulers.payable.retry_payable_notify = Box::new( - NotifyHandleMock::default() - .notify_params(¬ify_and_notify_later_params.retry_payables_notify) + subject.scan_schedulers.payable.retry_payable_notify_later = Box::new( + NotifyLaterHandleMock::default() + .notify_later_params(¬ify_and_notify_later_params.retry_payables_notify_later) .capture_msg_and_let_it_fly_on(), ); subject.scan_schedulers.payable.new_payable_notify = Box::new( @@ -3376,7 +3377,7 @@ mod tests { scan_for_new_payables_notify_params ); let scan_for_retry_payables_notify_params = notify_and_notify_later_params - .retry_payables_notify + .retry_payables_notify_later .lock() .unwrap(); assert!( @@ -3462,14 +3463,17 @@ mod tests { scan_for_new_payables_notify_params ); let scan_for_retry_payables_notify_params = notify_and_notify_later_params - .retry_payables_notify + .retry_payables_notify_later .lock() .unwrap(); assert_eq!( *scan_for_retry_payables_notify_params, - vec![ScanForRetryPayables { - response_skeleton_opt: None - }], + vec![( + ScanForRetryPayables { + response_skeleton_opt: None + }, + DEFAULT_RETRY_INTERVAL + )], ); } @@ -5092,8 +5096,8 @@ mod tests { Box::new(NotifyHandleMock::default().panic_on_schedule_attempt()); subject.scan_schedulers.payable.new_payable_notify_later = Box::new(NotifyLaterHandleMock::default().panic_on_schedule_attempt()); - subject.scan_schedulers.payable.retry_payable_notify = - Box::new(NotifyHandleMock::default().panic_on_schedule_attempt()); + subject.scan_schedulers.payable.retry_payable_notify_later = + Box::new(NotifyLaterHandleMock::default().panic_on_schedule_attempt()); let expected_tx = TxBuilder::default().hash(expected_hash.clone()).build(); let sent_payable = SentPayables { payment_procedure_result: Ok(BatchResults { @@ -5151,8 +5155,8 @@ mod tests { Box::new(NotifyHandleMock::default().panic_on_schedule_attempt()); subject.scan_schedulers.payable.new_payable_notify_later = Box::new(NotifyLaterHandleMock::default().panic_on_schedule_attempt()); - subject.scan_schedulers.payable.retry_payable_notify = - Box::new(NotifyHandleMock::default().panic_on_schedule_attempt()); + subject.scan_schedulers.payable.retry_payable_notify_later = + Box::new(NotifyLaterHandleMock::default().panic_on_schedule_attempt()); let expected_tx = TxBuilder::default().hash(expected_hash.clone()).build(); let sent_payable = SentPayables { payment_procedure_result: Ok(BatchResults { @@ -5186,7 +5190,7 @@ mod tests { init_test_logging(); let test_name = "retry_payable_scan_is_requested_to_be_repeated"; let finish_scan_params_arc = Arc::new(Mutex::new(vec![])); - let retry_payable_notify_params_arc = Arc::new(Mutex::new(vec![])); + let retry_payable_notify_later_params_arc = Arc::new(Mutex::new(vec![])); let system = System::new(test_name); let consuming_wallet = make_paying_wallet(b"paying wallet"); let mut subject = AccountantBuilder::default() @@ -5203,8 +5207,10 @@ mod tests { result: NextScanToRun::RetryPayableScan, }), ))); - subject.scan_schedulers.payable.retry_payable_notify = - Box::new(NotifyHandleMock::default().notify_params(&retry_payable_notify_params_arc)); + subject.scan_schedulers.payable.retry_payable_notify_later = Box::new( + NotifyLaterHandleMock::default() + .notify_later_params(&retry_payable_notify_later_params_arc), + ); subject.scan_schedulers.payable.new_payable_notify = Box::new(NotifyHandleMock::default().panic_on_schedule_attempt()); subject.scan_schedulers.payable.new_payable_notify_later = @@ -5230,9 +5236,10 @@ mod tests { let (actual_sent_payable, logger) = finish_scan_params.remove(0); assert_eq!(actual_sent_payable, sent_payable,); assert_using_the_same_logger(&logger, test_name, None); - let mut payable_notify_params = retry_payable_notify_params_arc.lock().unwrap(); - let scheduled_msg = payable_notify_params.remove(0); + let mut payable_notify_params = retry_payable_notify_later_params_arc.lock().unwrap(); + let (scheduled_msg, duration) = payable_notify_params.remove(0); assert_eq!(scheduled_msg, ScanForRetryPayables::default()); + assert_eq!(duration, DEFAULT_RETRY_INTERVAL); assert!( payable_notify_params.is_empty(), "Should be empty but {:?}", @@ -5247,7 +5254,7 @@ mod tests { let test_name = "accountant_in_automatic_mode_schedules_tx_retry_as_some_pending_payables_have_not_completed"; let finish_scan_params_arc = Arc::new(Mutex::new(vec![])); - let retry_payable_notify_params_arc = Arc::new(Mutex::new(vec![])); + let retry_payable_notify_later_params_arc = Arc::new(Mutex::new(vec![])); let mut subject = AccountantBuilder::default() .logger(Logger::new(test_name)) .build(); @@ -5265,8 +5272,10 @@ mod tests { Box::new(NotifyLaterHandleMock::default().panic_on_schedule_attempt()); subject.scan_schedulers.pending_payable.handle = Box::new(NotifyLaterHandleMock::default().panic_on_schedule_attempt()); - subject.scan_schedulers.payable.retry_payable_notify = - Box::new(NotifyHandleMock::default().notify_params(&retry_payable_notify_params_arc)); + subject.scan_schedulers.payable.retry_payable_notify_later = Box::new( + NotifyLaterHandleMock::default() + .notify_later_params(&retry_payable_notify_later_params_arc), + ); let system = System::new(test_name); let (mut msg, _) = make_tx_receipts_msg(vec![ SeedsToMakeUpPayableWithStatus { @@ -5288,12 +5297,15 @@ mod tests { let mut finish_scan_params = finish_scan_params_arc.lock().unwrap(); let (msg_actual, logger) = finish_scan_params.remove(0); assert_eq!(msg_actual, msg); - let retry_payable_notify_params = retry_payable_notify_params_arc.lock().unwrap(); + let retry_payable_notify_params = retry_payable_notify_later_params_arc.lock().unwrap(); assert_eq!( *retry_payable_notify_params, - vec![ScanForRetryPayables { - response_skeleton_opt: None - }] + vec![( + ScanForRetryPayables { + response_skeleton_opt: None + }, + DEFAULT_RETRY_INTERVAL + )] ); assert_using_the_same_logger(&logger, test_name, None) } @@ -5316,8 +5328,8 @@ mod tests { .replace_scanner(ScannerReplacement::PendingPayable(ReplacementType::Mock( pending_payable_scanner, ))); - subject.scan_schedulers.payable.retry_payable_notify = - Box::new(NotifyHandleMock::default().panic_on_schedule_attempt()); + subject.scan_schedulers.payable.retry_payable_notify_later = + Box::new(NotifyLaterHandleMock::default().panic_on_schedule_attempt()); subject.scan_schedulers.payable.new_payable_notify = Box::new(NotifyHandleMock::default().panic_on_schedule_attempt()); subject.scan_schedulers.payable.new_payable_notify_later = @@ -5382,8 +5394,8 @@ mod tests { .replace_scanner(ScannerReplacement::PendingPayable(ReplacementType::Mock( pending_payable_scanner, ))); - subject.scan_schedulers.payable.retry_payable_notify = - Box::new(NotifyHandleMock::default().panic_on_schedule_attempt()); + subject.scan_schedulers.payable.retry_payable_notify_later = + Box::new(NotifyLaterHandleMock::default().panic_on_schedule_attempt()); subject.scan_schedulers.payable.new_payable_notify = Box::new(NotifyHandleMock::default().panic_on_schedule_attempt()); subject.scan_schedulers.payable.new_payable_notify_later = @@ -5445,8 +5457,9 @@ mod tests { .replace_scanner(ScannerReplacement::PendingPayable(ReplacementType::Mock( pending_payable_scanner, ))); - subject.scan_schedulers.payable.retry_payable_notify = - Box::new(NotifyHandleMock::default().notify_params(&retry_payable_notify_params_arc)); + subject.scan_schedulers.payable.retry_payable_notify_later = Box::new( + NotifyLaterHandleMock::default().notify_later_params(&retry_payable_notify_params_arc), + ); subject.scan_schedulers.payable.new_payable_notify = Box::new(NotifyHandleMock::default().panic_on_schedule_attempt()); subject.scan_schedulers.payable.new_payable_notify_later = @@ -5470,9 +5483,12 @@ mod tests { let retry_payable_notify_params = retry_payable_notify_params_arc.lock().unwrap(); assert_eq!( *retry_payable_notify_params, - vec![ScanForRetryPayables { - response_skeleton_opt: Some(response_skeleton) - }] + vec![( + ScanForRetryPayables { + response_skeleton_opt: Some(response_skeleton) + }, + DEFAULT_RETRY_INTERVAL + )] ); assert_using_the_same_logger(&logger, test_name, None) } @@ -5889,28 +5905,32 @@ mod tests { Box::new( |_scanners: &mut Scanners, scan_schedulers: &mut ScanSchedulers| { // Setup - let notify_params_arc = Arc::new(Mutex::new(vec![])); - scan_schedulers.payable.retry_payable_notify = - Box::new(NotifyHandleMock::default().notify_params(¬ify_params_arc)); + let notify_later_params_arc = Arc::new(Mutex::new(vec![])); + scan_schedulers.payable.retry_payable_notify_later = Box::new( + NotifyLaterHandleMock::default().notify_later_params(¬ify_later_params_arc), + ); // Assertions Box::new(move |response_skeleton_opt| { - let notify_params = notify_params_arc.lock().unwrap(); + let notify_later_params = notify_later_params_arc.lock().unwrap(); match response_skeleton_opt { None => { // Response skeleton must be None assert_eq!( - *notify_params, - vec![ScanForRetryPayables { - response_skeleton_opt: None - }] + *notify_later_params, + vec![( + ScanForRetryPayables { + response_skeleton_opt: None + }, + DEFAULT_RETRY_INTERVAL + )] ) } Some(_) => { assert!( - notify_params.is_empty(), + notify_later_params.is_empty(), "Should be empty but contained {:?}", - notify_params + notify_later_params ) } } diff --git a/node/src/accountant/scanners/scan_schedulers.rs b/node/src/accountant/scanners/scan_schedulers.rs index 54a33dd2b..fd6a79901 100644 --- a/node/src/accountant/scanners/scan_schedulers.rs +++ b/node/src/accountant/scanners/scan_schedulers.rs @@ -16,6 +16,8 @@ use masq_lib::simple_clock::{SimpleClock, SimpleClockReal}; use std::fmt::{Debug, Display, Formatter}; use std::time::{Duration, SystemTime, UNIX_EPOCH}; +pub const DEFAULT_RETRY_INTERVAL: Duration = Duration::from_secs(5 * 60); + pub struct ScanSchedulers { pub payable: PayableScanScheduler, pub pending_payable: SimplePeriodicalScanScheduler, @@ -80,7 +82,7 @@ pub struct PayableScanScheduler { pub new_payable_notify_later: Box>, pub interval_computer: Box, pub new_payable_notify: Box>, - pub retry_payable_notify: Box>, + pub retry_payable_notify_later: Box>, } impl PayableScanScheduler { @@ -91,7 +93,7 @@ impl PayableScanScheduler { new_payable_interval, )), new_payable_notify: Box::new(NotifyHandleReal::default()), - retry_payable_notify: Box::new(NotifyHandleReal::default()), + retry_payable_notify_later: Box::new(NotifyLaterHandleReal::default()), } } @@ -138,12 +140,13 @@ impl PayableScanScheduler { ) { debug!(logger, "Scheduling a retry-payable scan asap"); - self.retry_payable_notify.notify( + let _ = self.retry_payable_notify_later.notify_later( ScanForRetryPayables { response_skeleton_opt, }, + DEFAULT_RETRY_INTERVAL, ctx, - ) + ); } } @@ -385,7 +388,7 @@ impl RescheduleScanOnErrorResolverReal { mod tests { use crate::accountant::scanners::scan_schedulers::{ NewPayableScanIntervalComputer, NewPayableScanIntervalComputerReal, PayableSequenceScanner, - ScanReschedulingAfterEarlyStop, ScanSchedulers, ScanTiming, + ScanReschedulingAfterEarlyStop, ScanSchedulers, ScanTiming, DEFAULT_RETRY_INTERVAL, }; use crate::accountant::scanners::test_utils::NewPayableScanIntervalComputerMock; use crate::accountant::scanners::{ManulTriggerError, StartScanError}; @@ -402,6 +405,11 @@ mod tests { use std::sync::{Arc, Mutex}; use std::time::{Duration, SystemTime, UNIX_EPOCH}; + #[test] + fn constants_have_correct_values() { + assert_eq!(DEFAULT_RETRY_INTERVAL, Duration::from_secs(5 * 60)); + } + #[test] fn scan_schedulers_are_initialized_correctly() { let scan_intervals = ScanIntervals { From af1a0759f35b06991c9ba6bccf469826a84e1bd1 Mon Sep 17 00:00:00 2001 From: utkarshg6 Date: Sun, 19 Oct 2025 21:55:09 +0530 Subject: [PATCH 08/20] GH-598: tests in accountant are passing --- node/src/accountant/mod.rs | 12 +++++++++++- node/src/accountant/scanners/scan_schedulers.rs | 13 ++++++++++--- node/src/db_config/persistent_configuration.rs | 1 + node/src/node_configurator/configurator.rs | 2 ++ .../unprivileged_parse_args_configuration.rs | 4 ++++ node/src/sub_lib/accountant.rs | 6 ++++++ node/src/sub_lib/combined_parameters.rs | 4 ++++ node/src/test_utils/mod.rs | 1 + 8 files changed, 39 insertions(+), 4 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index f37a6ea1b..cc7933099 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -1959,6 +1959,7 @@ mod tests { let mut config = bc_from_earning_wallet(make_wallet("some_wallet_address")); config.scan_intervals_opt = Some(ScanIntervals { payable_scan_interval: Duration::from_millis(10_000), + retry_payable_scan_interval: Duration::from_millis(1), receivable_scan_interval: Duration::from_millis(10_000), pending_payable_scan_interval: Duration::from_secs(100), }); @@ -2282,6 +2283,7 @@ mod tests { ]) .build(); subject.scan_schedulers.automatic_scans_enabled = false; + subject.scan_schedulers.payable.retry_payable_scan_interval = Duration::from_millis(1); let (blockchain_bridge, _, blockchain_bridge_recording_arc) = make_recorder(); let (ui_gateway, _, ui_gateway_recording_arc) = make_recorder(); let ui_gateway = @@ -2680,6 +2682,7 @@ mod tests { let mut config = bc_from_earning_wallet(make_wallet("earning_wallet")); config.scan_intervals_opt = Some(ScanIntervals { payable_scan_interval: Duration::from_millis(10_000), + retry_payable_scan_interval: Duration::from_millis(1), pending_payable_scan_interval: Duration::from_millis(2_000), receivable_scan_interval: Duration::from_millis(10_000), }); @@ -2728,6 +2731,7 @@ mod tests { let mut config = bc_from_earning_wallet(make_wallet("earning_wallet")); config.scan_intervals_opt = Some(ScanIntervals { payable_scan_interval: Duration::from_millis(10_000), + retry_payable_scan_interval: Duration::from_millis(1), pending_payable_scan_interval: Duration::from_millis(2_000), receivable_scan_interval: Duration::from_millis(10_000), }); @@ -3152,6 +3156,7 @@ mod tests { receivable_scanner, payable_scanner, ); + let retry_payble_scan_interval = Duration::from_millis(1); let pending_payable_scan_interval = Duration::from_secs(3600); let receivable_scan_interval = Duration::from_secs(3600); let pending_payable_notify_later_handle_mock = NotifyLaterHandleMock::default() @@ -3160,6 +3165,7 @@ mod tests { .stop_system_on_count_received(1); subject.scan_schedulers.pending_payable.handle = Box::new(pending_payable_notify_later_handle_mock); + subject.scan_schedulers.payable.retry_payable_scan_interval = retry_payble_scan_interval; subject.scan_schedulers.pending_payable.interval = pending_payable_scan_interval; subject.scan_schedulers.payable.new_payable_notify_later = Box::new( NotifyLaterHandleMock::default() @@ -3472,7 +3478,7 @@ mod tests { ScanForRetryPayables { response_skeleton_opt: None }, - DEFAULT_RETRY_INTERVAL + Duration::from_millis(1) )], ); } @@ -3650,6 +3656,7 @@ mod tests { let mut config = bc_from_earning_wallet(earning_wallet.clone()); config.scan_intervals_opt = Some(ScanIntervals { payable_scan_interval: Duration::from_secs(100), + retry_payable_scan_interval: Duration::from_millis(1), pending_payable_scan_interval: Duration::from_secs(10), receivable_scan_interval: Duration::from_millis(99), }); @@ -3929,6 +3936,7 @@ mod tests { // This simply means that we're gonna surplus this value (it abides by how many pending // payable cycles have to go in between before the lastly submitted txs are confirmed), payable_scan_interval: Duration::from_millis(10), + retry_payable_scan_interval: Duration::from_millis(1), pending_payable_scan_interval: Duration::from_millis(50), receivable_scan_interval: Duration::from_secs(100), // We'll never run this scanner }); @@ -4017,6 +4025,7 @@ mod tests { let mut config = bc_from_earning_wallet(make_wallet("hi")); config.scan_intervals_opt = Some(ScanIntervals { payable_scan_interval: Duration::from_millis(100), + retry_payable_scan_interval: Duration::from_millis(1), pending_payable_scan_interval: Duration::from_millis(50), receivable_scan_interval: Duration::from_millis(100), }); @@ -4139,6 +4148,7 @@ mod tests { let consuming_wallet = make_paying_wallet(b"consuming"); config.scan_intervals_opt = Some(ScanIntervals { payable_scan_interval: Duration::from_secs(50_000), + retry_payable_scan_interval: Duration::from_millis(1), pending_payable_scan_interval: Duration::from_secs(10_000), receivable_scan_interval: Duration::from_secs(50_000), }); diff --git a/node/src/accountant/scanners/scan_schedulers.rs b/node/src/accountant/scanners/scan_schedulers.rs index fd6a79901..1b140a799 100644 --- a/node/src/accountant/scanners/scan_schedulers.rs +++ b/node/src/accountant/scanners/scan_schedulers.rs @@ -29,7 +29,10 @@ pub struct ScanSchedulers { impl ScanSchedulers { pub fn new(scan_intervals: ScanIntervals, automatic_scans_enabled: bool) -> Self { Self { - payable: PayableScanScheduler::new(scan_intervals.payable_scan_interval), + payable: PayableScanScheduler::new( + scan_intervals.payable_scan_interval, + scan_intervals.retry_payable_scan_interval, + ), pending_payable: SimplePeriodicalScanScheduler::new( scan_intervals.pending_payable_scan_interval, ), @@ -83,10 +86,11 @@ pub struct PayableScanScheduler { pub interval_computer: Box, pub new_payable_notify: Box>, pub retry_payable_notify_later: Box>, + pub retry_payable_scan_interval: Duration, } impl PayableScanScheduler { - fn new(new_payable_interval: Duration) -> Self { + fn new(new_payable_interval: Duration, retry_payable_scan_interval: Duration) -> Self { Self { new_payable_notify_later: Box::new(NotifyLaterHandleReal::default()), interval_computer: Box::new(NewPayableScanIntervalComputerReal::new( @@ -94,6 +98,7 @@ impl PayableScanScheduler { )), new_payable_notify: Box::new(NotifyHandleReal::default()), retry_payable_notify_later: Box::new(NotifyLaterHandleReal::default()), + retry_payable_scan_interval, } } @@ -139,12 +144,13 @@ impl PayableScanScheduler { logger: &Logger, ) { debug!(logger, "Scheduling a retry-payable scan asap"); + let delay = self.retry_payable_scan_interval; let _ = self.retry_payable_notify_later.notify_later( ScanForRetryPayables { response_skeleton_opt, }, - DEFAULT_RETRY_INTERVAL, + delay, ctx, ); } @@ -414,6 +420,7 @@ mod tests { fn scan_schedulers_are_initialized_correctly() { let scan_intervals = ScanIntervals { payable_scan_interval: Duration::from_secs(14), + retry_payable_scan_interval: Default::default(), pending_payable_scan_interval: Duration::from_secs(2), receivable_scan_interval: Duration::from_secs(7), }; diff --git a/node/src/db_config/persistent_configuration.rs b/node/src/db_config/persistent_configuration.rs index 8567d7807..2b87147d1 100644 --- a/node/src/db_config/persistent_configuration.rs +++ b/node/src/db_config/persistent_configuration.rs @@ -2291,6 +2291,7 @@ mod tests { "60|5|50", ScanIntervals { payable_scan_interval: Duration::from_secs(60), + retry_payable_scan_interval: Duration::from_millis(1), pending_payable_scan_interval: Duration::from_secs(5), receivable_scan_interval: Duration::from_secs(50), } diff --git a/node/src/node_configurator/configurator.rs b/node/src/node_configurator/configurator.rs index 95ad7115e..852729b20 100644 --- a/node/src/node_configurator/configurator.rs +++ b/node/src/node_configurator/configurator.rs @@ -2626,6 +2626,7 @@ mod tests { })) .scan_intervals_result(Ok(ScanIntervals { payable_scan_interval: Duration::from_secs(125), + retry_payable_scan_interval: Duration::from_millis(1), pending_payable_scan_interval: Duration::from_secs(122), receivable_scan_interval: Duration::from_secs(128), })) @@ -2777,6 +2778,7 @@ mod tests { })) .scan_intervals_result(Ok(ScanIntervals { payable_scan_interval: Default::default(), + retry_payable_scan_interval: Default::default(), pending_payable_scan_interval: Default::default(), receivable_scan_interval: Default::default(), })) diff --git a/node/src/node_configurator/unprivileged_parse_args_configuration.rs b/node/src/node_configurator/unprivileged_parse_args_configuration.rs index 313bf3048..35d682050 100644 --- a/node/src/node_configurator/unprivileged_parse_args_configuration.rs +++ b/node/src/node_configurator/unprivileged_parse_args_configuration.rs @@ -1829,6 +1829,7 @@ mod tests { configure_default_persistent_config(RATE_PACK | MAPPING_PROTOCOL) .scan_intervals_result(Ok(ScanIntervals { payable_scan_interval: Duration::from_secs(101), + retry_payable_scan_interval: Duration::from_millis(1), pending_payable_scan_interval: Duration::from_secs(33), receivable_scan_interval: Duration::from_secs(102), })) @@ -1857,6 +1858,7 @@ mod tests { let expected_scan_intervals = ScanIntervals { payable_scan_interval: Duration::from_secs(180), + retry_payable_scan_interval: Duration::from_millis(1), pending_payable_scan_interval: Duration::from_secs(50), receivable_scan_interval: Duration::from_secs(130), }; @@ -1907,6 +1909,7 @@ mod tests { configure_default_persistent_config(RATE_PACK | MAPPING_PROTOCOL) .scan_intervals_result(Ok(ScanIntervals { payable_scan_interval: Duration::from_secs(180), + retry_payable_scan_interval: Duration::from_millis(1), pending_payable_scan_interval: Duration::from_secs(15), receivable_scan_interval: Duration::from_secs(130), })) @@ -1939,6 +1942,7 @@ mod tests { }; let expected_scan_intervals = ScanIntervals { payable_scan_interval: Duration::from_secs(180), + retry_payable_scan_interval: Duration::from_millis(1), pending_payable_scan_interval: Duration::from_secs(15), receivable_scan_interval: Duration::from_secs(130), }; diff --git a/node/src/sub_lib/accountant.rs b/node/src/sub_lib/accountant.rs index 039b1fe4f..b8eb5625f 100644 --- a/node/src/sub_lib/accountant.rs +++ b/node/src/sub_lib/accountant.rs @@ -5,6 +5,7 @@ use crate::accountant::db_access_objects::payable_dao::PayableDaoFactory; use crate::accountant::db_access_objects::receivable_dao::ReceivableDaoFactory; use crate::accountant::db_access_objects::sent_payable_dao::SentPayableDaoFactory; use crate::accountant::scanners::payable_scanner::msgs::PricedTemplatesMessage; +use crate::accountant::scanners::scan_schedulers::DEFAULT_RETRY_INTERVAL; use crate::accountant::{ checked_conversion, Accountant, ReceivedPayments, ScanError, SentPayables, TxReceiptsMessage, }; @@ -77,6 +78,7 @@ pub struct DaoFactories { #[derive(PartialEq, Eq, Debug, Clone, Copy)] pub struct ScanIntervals { pub payable_scan_interval: Duration, + pub retry_payable_scan_interval: Duration, pub pending_payable_scan_interval: Duration, pub receivable_scan_interval: Duration, } @@ -85,6 +87,7 @@ impl ScanIntervals { pub fn compute_default(chain: Chain) -> Self { Self { payable_scan_interval: Duration::from_secs(600), + retry_payable_scan_interval: DEFAULT_RETRY_INTERVAL, pending_payable_scan_interval: Duration::from_secs( chain.rec().default_pending_payable_interval_sec, ), @@ -204,6 +207,7 @@ pub enum DetailedScanType { #[cfg(test)] mod tests { + use crate::accountant::scanners::scan_schedulers::DEFAULT_RETRY_INTERVAL; use crate::accountant::test_utils::AccountantBuilder; use crate::accountant::{checked_conversion, Accountant}; use crate::sub_lib::accountant::{ @@ -319,6 +323,7 @@ mod tests { result_a, ScanIntervals { payable_scan_interval: Duration::from_secs(600), + retry_payable_scan_interval: DEFAULT_RETRY_INTERVAL, pending_payable_scan_interval: Duration::from_secs( chain_a.rec().default_pending_payable_interval_sec ), @@ -329,6 +334,7 @@ mod tests { result_b, ScanIntervals { payable_scan_interval: Duration::from_secs(600), + retry_payable_scan_interval: DEFAULT_RETRY_INTERVAL, pending_payable_scan_interval: Duration::from_secs( chain_b.rec().default_pending_payable_interval_sec ), diff --git a/node/src/sub_lib/combined_parameters.rs b/node/src/sub_lib/combined_parameters.rs index bd26eb627..2d1faebe7 100644 --- a/node/src/sub_lib/combined_parameters.rs +++ b/node/src/sub_lib/combined_parameters.rs @@ -178,6 +178,7 @@ impl CombinedParams { &parsed_values, Duration::from_secs, "payable_scan_interval", + "retry_payable_scan_interval", "pending_payable_scan_interval", "receivable_scan_interval" ))) @@ -305,6 +306,7 @@ fn unreachable() -> ! { #[cfg(test)] mod tests { use super::*; + use crate::accountant::scanners::scan_schedulers::DEFAULT_RETRY_INTERVAL; use crate::sub_lib::combined_parameters::CombinedParamsDataTypes::U128; use crate::sub_lib::neighborhood::DEFAULT_RATE_PACK; use crate::test_utils::unshared_test_utils::TEST_SCAN_INTERVALS; @@ -559,6 +561,7 @@ mod tests { result, ScanIntervals { payable_scan_interval: Duration::from_secs(115), + retry_payable_scan_interval: DEFAULT_RETRY_INTERVAL, pending_payable_scan_interval: Duration::from_secs(55), receivable_scan_interval: Duration::from_secs(113) } @@ -569,6 +572,7 @@ mod tests { fn scan_intervals_to_combined_params() { let scan_intervals = ScanIntervals { payable_scan_interval: Duration::from_secs(90), + retry_payable_scan_interval: DEFAULT_RETRY_INTERVAL, pending_payable_scan_interval: Duration::from_secs(40), receivable_scan_interval: Duration::from_secs(100), }; diff --git a/node/src/test_utils/mod.rs b/node/src/test_utils/mod.rs index eeb2c9560..7605d2238 100644 --- a/node/src/test_utils/mod.rs +++ b/node/src/test_utils/mod.rs @@ -627,6 +627,7 @@ pub mod unshared_test_utils { lazy_static! { pub static ref TEST_SCAN_INTERVALS: ScanIntervals = ScanIntervals { payable_scan_interval: Duration::from_secs(600), + retry_payable_scan_interval: Duration::from_millis(1), pending_payable_scan_interval: Duration::from_secs(360), receivable_scan_interval: Duration::from_secs(600), }; From 268eebaaf6b4d1f12331730a083e97c0dfa05bdb Mon Sep 17 00:00:00 2001 From: utkarshg6 Date: Mon, 20 Oct 2025 12:03:09 +0530 Subject: [PATCH 09/20] GH-598: retry payable scan interval is half the duration of payable scan interval --- node/src/accountant/mod.rs | 16 ++++---------- .../accountant/scanners/scan_schedulers.rs | 22 +++++-------------- .../src/db_config/persistent_configuration.rs | 1 - node/src/node_configurator/configurator.rs | 2 -- .../unprivileged_parse_args_configuration.rs | 4 ---- node/src/sub_lib/accountant.rs | 6 ----- node/src/sub_lib/combined_parameters.rs | 4 ---- node/src/test_utils/mod.rs | 1 - 8 files changed, 10 insertions(+), 46 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index cc7933099..31f3033b5 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -1319,7 +1319,6 @@ mod tests { use crate::accountant::scanners::pending_payable_scanner::utils::TxByTable; use crate::accountant::scanners::scan_schedulers::{ NewPayableScanIntervalComputer, NewPayableScanIntervalComputerReal, ScanTiming, - DEFAULT_RETRY_INTERVAL, }; use crate::accountant::scanners::test_utils::{ MarkScanner, NewPayableScanIntervalComputerMock, PendingPayableCacheMock, ReplacementType, @@ -1959,7 +1958,6 @@ mod tests { let mut config = bc_from_earning_wallet(make_wallet("some_wallet_address")); config.scan_intervals_opt = Some(ScanIntervals { payable_scan_interval: Duration::from_millis(10_000), - retry_payable_scan_interval: Duration::from_millis(1), receivable_scan_interval: Duration::from_millis(10_000), pending_payable_scan_interval: Duration::from_secs(100), }); @@ -2682,7 +2680,6 @@ mod tests { let mut config = bc_from_earning_wallet(make_wallet("earning_wallet")); config.scan_intervals_opt = Some(ScanIntervals { payable_scan_interval: Duration::from_millis(10_000), - retry_payable_scan_interval: Duration::from_millis(1), pending_payable_scan_interval: Duration::from_millis(2_000), receivable_scan_interval: Duration::from_millis(10_000), }); @@ -2731,7 +2728,6 @@ mod tests { let mut config = bc_from_earning_wallet(make_wallet("earning_wallet")); config.scan_intervals_opt = Some(ScanIntervals { payable_scan_interval: Duration::from_millis(10_000), - retry_payable_scan_interval: Duration::from_millis(1), pending_payable_scan_interval: Duration::from_millis(2_000), receivable_scan_interval: Duration::from_millis(10_000), }); @@ -3656,7 +3652,6 @@ mod tests { let mut config = bc_from_earning_wallet(earning_wallet.clone()); config.scan_intervals_opt = Some(ScanIntervals { payable_scan_interval: Duration::from_secs(100), - retry_payable_scan_interval: Duration::from_millis(1), pending_payable_scan_interval: Duration::from_secs(10), receivable_scan_interval: Duration::from_millis(99), }); @@ -3936,7 +3931,6 @@ mod tests { // This simply means that we're gonna surplus this value (it abides by how many pending // payable cycles have to go in between before the lastly submitted txs are confirmed), payable_scan_interval: Duration::from_millis(10), - retry_payable_scan_interval: Duration::from_millis(1), pending_payable_scan_interval: Duration::from_millis(50), receivable_scan_interval: Duration::from_secs(100), // We'll never run this scanner }); @@ -4025,7 +4019,6 @@ mod tests { let mut config = bc_from_earning_wallet(make_wallet("hi")); config.scan_intervals_opt = Some(ScanIntervals { payable_scan_interval: Duration::from_millis(100), - retry_payable_scan_interval: Duration::from_millis(1), pending_payable_scan_interval: Duration::from_millis(50), receivable_scan_interval: Duration::from_millis(100), }); @@ -4148,7 +4141,6 @@ mod tests { let consuming_wallet = make_paying_wallet(b"consuming"); config.scan_intervals_opt = Some(ScanIntervals { payable_scan_interval: Duration::from_secs(50_000), - retry_payable_scan_interval: Duration::from_millis(1), pending_payable_scan_interval: Duration::from_secs(10_000), receivable_scan_interval: Duration::from_secs(50_000), }); @@ -5249,7 +5241,7 @@ mod tests { let mut payable_notify_params = retry_payable_notify_later_params_arc.lock().unwrap(); let (scheduled_msg, duration) = payable_notify_params.remove(0); assert_eq!(scheduled_msg, ScanForRetryPayables::default()); - assert_eq!(duration, DEFAULT_RETRY_INTERVAL); + assert_eq!(duration, Duration::from_secs(5 * 60)); assert!( payable_notify_params.is_empty(), "Should be empty but {:?}", @@ -5314,7 +5306,7 @@ mod tests { ScanForRetryPayables { response_skeleton_opt: None }, - DEFAULT_RETRY_INTERVAL + Duration::from_secs(5 * 60) )] ); assert_using_the_same_logger(&logger, test_name, None) @@ -5497,7 +5489,7 @@ mod tests { ScanForRetryPayables { response_skeleton_opt: Some(response_skeleton) }, - DEFAULT_RETRY_INTERVAL + Duration::from_secs(5 * 60) )] ); assert_using_the_same_logger(&logger, test_name, None) @@ -5932,7 +5924,7 @@ mod tests { ScanForRetryPayables { response_skeleton_opt: None }, - DEFAULT_RETRY_INTERVAL + Duration::from_secs(5 * 60) )] ) } diff --git a/node/src/accountant/scanners/scan_schedulers.rs b/node/src/accountant/scanners/scan_schedulers.rs index 1b140a799..97a3edd62 100644 --- a/node/src/accountant/scanners/scan_schedulers.rs +++ b/node/src/accountant/scanners/scan_schedulers.rs @@ -14,10 +14,9 @@ use masq_lib::logger::Logger; use masq_lib::messages::ScanType; use masq_lib::simple_clock::{SimpleClock, SimpleClockReal}; use std::fmt::{Debug, Display, Formatter}; +use std::ops::Div; use std::time::{Duration, SystemTime, UNIX_EPOCH}; -pub const DEFAULT_RETRY_INTERVAL: Duration = Duration::from_secs(5 * 60); - pub struct ScanSchedulers { pub payable: PayableScanScheduler, pub pending_payable: SimplePeriodicalScanScheduler, @@ -29,10 +28,7 @@ pub struct ScanSchedulers { impl ScanSchedulers { pub fn new(scan_intervals: ScanIntervals, automatic_scans_enabled: bool) -> Self { Self { - payable: PayableScanScheduler::new( - scan_intervals.payable_scan_interval, - scan_intervals.retry_payable_scan_interval, - ), + payable: PayableScanScheduler::new(scan_intervals.payable_scan_interval), pending_payable: SimplePeriodicalScanScheduler::new( scan_intervals.pending_payable_scan_interval, ), @@ -90,15 +86,15 @@ pub struct PayableScanScheduler { } impl PayableScanScheduler { - fn new(new_payable_interval: Duration, retry_payable_scan_interval: Duration) -> Self { + fn new(payable_scan_interval: Duration) -> Self { Self { new_payable_notify_later: Box::new(NotifyLaterHandleReal::default()), interval_computer: Box::new(NewPayableScanIntervalComputerReal::new( - new_payable_interval, + payable_scan_interval, )), new_payable_notify: Box::new(NotifyHandleReal::default()), retry_payable_notify_later: Box::new(NotifyLaterHandleReal::default()), - retry_payable_scan_interval, + retry_payable_scan_interval: payable_scan_interval.div(2), } } @@ -394,7 +390,7 @@ impl RescheduleScanOnErrorResolverReal { mod tests { use crate::accountant::scanners::scan_schedulers::{ NewPayableScanIntervalComputer, NewPayableScanIntervalComputerReal, PayableSequenceScanner, - ScanReschedulingAfterEarlyStop, ScanSchedulers, ScanTiming, DEFAULT_RETRY_INTERVAL, + ScanReschedulingAfterEarlyStop, ScanSchedulers, ScanTiming, }; use crate::accountant::scanners::test_utils::NewPayableScanIntervalComputerMock; use crate::accountant::scanners::{ManulTriggerError, StartScanError}; @@ -411,16 +407,10 @@ mod tests { use std::sync::{Arc, Mutex}; use std::time::{Duration, SystemTime, UNIX_EPOCH}; - #[test] - fn constants_have_correct_values() { - assert_eq!(DEFAULT_RETRY_INTERVAL, Duration::from_secs(5 * 60)); - } - #[test] fn scan_schedulers_are_initialized_correctly() { let scan_intervals = ScanIntervals { payable_scan_interval: Duration::from_secs(14), - retry_payable_scan_interval: Default::default(), pending_payable_scan_interval: Duration::from_secs(2), receivable_scan_interval: Duration::from_secs(7), }; diff --git a/node/src/db_config/persistent_configuration.rs b/node/src/db_config/persistent_configuration.rs index 2b87147d1..8567d7807 100644 --- a/node/src/db_config/persistent_configuration.rs +++ b/node/src/db_config/persistent_configuration.rs @@ -2291,7 +2291,6 @@ mod tests { "60|5|50", ScanIntervals { payable_scan_interval: Duration::from_secs(60), - retry_payable_scan_interval: Duration::from_millis(1), pending_payable_scan_interval: Duration::from_secs(5), receivable_scan_interval: Duration::from_secs(50), } diff --git a/node/src/node_configurator/configurator.rs b/node/src/node_configurator/configurator.rs index 852729b20..95ad7115e 100644 --- a/node/src/node_configurator/configurator.rs +++ b/node/src/node_configurator/configurator.rs @@ -2626,7 +2626,6 @@ mod tests { })) .scan_intervals_result(Ok(ScanIntervals { payable_scan_interval: Duration::from_secs(125), - retry_payable_scan_interval: Duration::from_millis(1), pending_payable_scan_interval: Duration::from_secs(122), receivable_scan_interval: Duration::from_secs(128), })) @@ -2778,7 +2777,6 @@ mod tests { })) .scan_intervals_result(Ok(ScanIntervals { payable_scan_interval: Default::default(), - retry_payable_scan_interval: Default::default(), pending_payable_scan_interval: Default::default(), receivable_scan_interval: Default::default(), })) diff --git a/node/src/node_configurator/unprivileged_parse_args_configuration.rs b/node/src/node_configurator/unprivileged_parse_args_configuration.rs index 35d682050..313bf3048 100644 --- a/node/src/node_configurator/unprivileged_parse_args_configuration.rs +++ b/node/src/node_configurator/unprivileged_parse_args_configuration.rs @@ -1829,7 +1829,6 @@ mod tests { configure_default_persistent_config(RATE_PACK | MAPPING_PROTOCOL) .scan_intervals_result(Ok(ScanIntervals { payable_scan_interval: Duration::from_secs(101), - retry_payable_scan_interval: Duration::from_millis(1), pending_payable_scan_interval: Duration::from_secs(33), receivable_scan_interval: Duration::from_secs(102), })) @@ -1858,7 +1857,6 @@ mod tests { let expected_scan_intervals = ScanIntervals { payable_scan_interval: Duration::from_secs(180), - retry_payable_scan_interval: Duration::from_millis(1), pending_payable_scan_interval: Duration::from_secs(50), receivable_scan_interval: Duration::from_secs(130), }; @@ -1909,7 +1907,6 @@ mod tests { configure_default_persistent_config(RATE_PACK | MAPPING_PROTOCOL) .scan_intervals_result(Ok(ScanIntervals { payable_scan_interval: Duration::from_secs(180), - retry_payable_scan_interval: Duration::from_millis(1), pending_payable_scan_interval: Duration::from_secs(15), receivable_scan_interval: Duration::from_secs(130), })) @@ -1942,7 +1939,6 @@ mod tests { }; let expected_scan_intervals = ScanIntervals { payable_scan_interval: Duration::from_secs(180), - retry_payable_scan_interval: Duration::from_millis(1), pending_payable_scan_interval: Duration::from_secs(15), receivable_scan_interval: Duration::from_secs(130), }; diff --git a/node/src/sub_lib/accountant.rs b/node/src/sub_lib/accountant.rs index b8eb5625f..039b1fe4f 100644 --- a/node/src/sub_lib/accountant.rs +++ b/node/src/sub_lib/accountant.rs @@ -5,7 +5,6 @@ use crate::accountant::db_access_objects::payable_dao::PayableDaoFactory; use crate::accountant::db_access_objects::receivable_dao::ReceivableDaoFactory; use crate::accountant::db_access_objects::sent_payable_dao::SentPayableDaoFactory; use crate::accountant::scanners::payable_scanner::msgs::PricedTemplatesMessage; -use crate::accountant::scanners::scan_schedulers::DEFAULT_RETRY_INTERVAL; use crate::accountant::{ checked_conversion, Accountant, ReceivedPayments, ScanError, SentPayables, TxReceiptsMessage, }; @@ -78,7 +77,6 @@ pub struct DaoFactories { #[derive(PartialEq, Eq, Debug, Clone, Copy)] pub struct ScanIntervals { pub payable_scan_interval: Duration, - pub retry_payable_scan_interval: Duration, pub pending_payable_scan_interval: Duration, pub receivable_scan_interval: Duration, } @@ -87,7 +85,6 @@ impl ScanIntervals { pub fn compute_default(chain: Chain) -> Self { Self { payable_scan_interval: Duration::from_secs(600), - retry_payable_scan_interval: DEFAULT_RETRY_INTERVAL, pending_payable_scan_interval: Duration::from_secs( chain.rec().default_pending_payable_interval_sec, ), @@ -207,7 +204,6 @@ pub enum DetailedScanType { #[cfg(test)] mod tests { - use crate::accountant::scanners::scan_schedulers::DEFAULT_RETRY_INTERVAL; use crate::accountant::test_utils::AccountantBuilder; use crate::accountant::{checked_conversion, Accountant}; use crate::sub_lib::accountant::{ @@ -323,7 +319,6 @@ mod tests { result_a, ScanIntervals { payable_scan_interval: Duration::from_secs(600), - retry_payable_scan_interval: DEFAULT_RETRY_INTERVAL, pending_payable_scan_interval: Duration::from_secs( chain_a.rec().default_pending_payable_interval_sec ), @@ -334,7 +329,6 @@ mod tests { result_b, ScanIntervals { payable_scan_interval: Duration::from_secs(600), - retry_payable_scan_interval: DEFAULT_RETRY_INTERVAL, pending_payable_scan_interval: Duration::from_secs( chain_b.rec().default_pending_payable_interval_sec ), diff --git a/node/src/sub_lib/combined_parameters.rs b/node/src/sub_lib/combined_parameters.rs index 2d1faebe7..bd26eb627 100644 --- a/node/src/sub_lib/combined_parameters.rs +++ b/node/src/sub_lib/combined_parameters.rs @@ -178,7 +178,6 @@ impl CombinedParams { &parsed_values, Duration::from_secs, "payable_scan_interval", - "retry_payable_scan_interval", "pending_payable_scan_interval", "receivable_scan_interval" ))) @@ -306,7 +305,6 @@ fn unreachable() -> ! { #[cfg(test)] mod tests { use super::*; - use crate::accountant::scanners::scan_schedulers::DEFAULT_RETRY_INTERVAL; use crate::sub_lib::combined_parameters::CombinedParamsDataTypes::U128; use crate::sub_lib::neighborhood::DEFAULT_RATE_PACK; use crate::test_utils::unshared_test_utils::TEST_SCAN_INTERVALS; @@ -561,7 +559,6 @@ mod tests { result, ScanIntervals { payable_scan_interval: Duration::from_secs(115), - retry_payable_scan_interval: DEFAULT_RETRY_INTERVAL, pending_payable_scan_interval: Duration::from_secs(55), receivable_scan_interval: Duration::from_secs(113) } @@ -572,7 +569,6 @@ mod tests { fn scan_intervals_to_combined_params() { let scan_intervals = ScanIntervals { payable_scan_interval: Duration::from_secs(90), - retry_payable_scan_interval: DEFAULT_RETRY_INTERVAL, pending_payable_scan_interval: Duration::from_secs(40), receivable_scan_interval: Duration::from_secs(100), }; diff --git a/node/src/test_utils/mod.rs b/node/src/test_utils/mod.rs index 7605d2238..eeb2c9560 100644 --- a/node/src/test_utils/mod.rs +++ b/node/src/test_utils/mod.rs @@ -627,7 +627,6 @@ pub mod unshared_test_utils { lazy_static! { pub static ref TEST_SCAN_INTERVALS: ScanIntervals = ScanIntervals { payable_scan_interval: Duration::from_secs(600), - retry_payable_scan_interval: Duration::from_millis(1), pending_payable_scan_interval: Duration::from_secs(360), receivable_scan_interval: Duration::from_secs(600), }; From 77a1d1c6e690bc2de9d2f09e8ec61645f1406240 Mon Sep 17 00:00:00 2001 From: utkarshg6 Date: Mon, 20 Oct 2025 13:17:34 +0530 Subject: [PATCH 10/20] GH-598: add public rpc url for eth mainnet --- node/tests/contract_test.rs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/node/tests/contract_test.rs b/node/tests/contract_test.rs index 42d6fce37..35b7e0f85 100644 --- a/node/tests/contract_test.rs +++ b/node/tests/contract_test.rs @@ -133,7 +133,10 @@ fn masq_erc20_contract_exists_on_polygon_mainnet_integration() { #[test] fn masq_erc20_contract_exists_on_ethereum_mainnet_integration() { - let blockchain_urls = vec!["https://mainnet.infura.io/v3/0ead23143b174f6983c76f69ddcf4026"]; + let blockchain_urls = vec![ + "https://eth.llamarpc.com", + "https://mainnet.infura.io/v3/0ead23143b174f6983c76f69ddcf4026", + ]; let chain = Chain::EthMainnet; let assertion_body = |url, chain| assert_contract_existence(url, chain, "MASQ", 18); @@ -207,7 +210,10 @@ fn assert_total_supply( #[test] fn max_token_supply_matches_corresponding_constant_integration() { - let blockchain_urls = vec!["https://mainnet.infura.io/v3/0ead23143b174f6983c76f69ddcf4026"]; + let blockchain_urls = vec![ + "https://eth.llamarpc.com", + "https://mainnet.infura.io/v3/0ead23143b174f6983c76f69ddcf4026", + ]; let chain = Chain::EthMainnet; let assertion_body = |url, chain| assert_total_supply(url, chain, MASQ_TOTAL_SUPPLY); From 25af8cba16e8ef2f748e764d6abecbc8b17ea500 Mon Sep 17 00:00:00 2001 From: Bert Date: Sun, 2 Nov 2025 13:51:43 +0100 Subject: [PATCH 11/20] GH-598: transaction count param from pending to latest --- .../blockchain_interface_web3/lower_level_interface_web3.rs | 4 ++-- .../blockchain_interface/blockchain_interface_web3/mod.rs | 2 ++ 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/node/src/blockchain/blockchain_interface/blockchain_interface_web3/lower_level_interface_web3.rs b/node/src/blockchain/blockchain_interface/blockchain_interface_web3/lower_level_interface_web3.rs index 7a4d6ddfb..e2c6f4dcc 100644 --- a/node/src/blockchain/blockchain_interface/blockchain_interface_web3/lower_level_interface_web3.rs +++ b/node/src/blockchain/blockchain_interface/blockchain_interface_web3/lower_level_interface_web3.rs @@ -9,7 +9,7 @@ use futures::Future; use serde_json::Value; use web3::contract::{Contract, Options}; use web3::transports::{Batch, Http}; -use web3::types::{Address, BlockNumber, Filter, Log}; +use web3::types::{Address, Filter, Log}; use web3::{Error, Web3}; pub struct LowBlockchainIntWeb3 { @@ -68,7 +68,7 @@ impl LowBlockchainInt for LowBlockchainIntWeb3 { Box::new( self.web3 .eth() - .transaction_count(address, Some(BlockNumber::Pending)) + .transaction_count(address, None) .map_err(move |e| QueryFailed(format!("{} for wallet {}", e, address))), ) } diff --git a/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs b/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs index 9249c6ee0..18e95ee29 100644 --- a/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs +++ b/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs @@ -279,6 +279,8 @@ impl BlockchainInterface for BlockchainInterfaceWeb3 { get_transaction_id .map_err(LocalPayableError::TransactionID) .and_then(move |latest_nonce| { + debug!(logger, "Latest nonce: {:?}", latest_nonce); + let templates = SignableTxTemplates::new(priced_templates, latest_nonce.as_u64()); From a151e96a19ec81845f0f7e0415d7b5d7d25c2e99 Mon Sep 17 00:00:00 2001 From: dnwiebe Date: Wed, 5 Nov 2025 08:14:47 -0500 Subject: [PATCH 12/20] Breaking change to remove RRID from CORES packages (#740) * Fixed the AddRouteResultMessage panic problem too * Preemptive review issues * Formatting * example.com -> www.example.com * Formatting * Added some debug logging to track RRI * Formatting * Clippy * Made RRI logs more consistent and increased straggler timeout to 30s * Typo corrections * Interim commit * Tests pass * Unit tests pass * Interim commit: a real mess * Proxy Server tests passing; some cleanup yet to do * Unit tests are all passing * Unit and multinode tests pass * All the tests pass now * Tests passing except one in proxy_client * RRIDs are completely gone; hostnames are mandatory. * MOre review issues * Review issues * Test is passing now, and code is cleaner * Cleaned up some code * More review issues * Formatting * Added packet describers * Error message change. * log messages enhanced of StreamKey --------- Co-authored-by: czarte --- .gitignore | 1 + .../tests/connection_termination_test.rs | 73 +- .../tests/self_test.rs | 11 +- node/src/dispatcher.rs | 28 +- node/src/hopper/consuming_service.rs | 23 +- node/src/hopper/live_cores_package.rs | 22 +- node/src/hopper/mod.rs | 13 +- node/src/hopper/routing_service.rs | 134 +- node/src/neighborhood/mod.rs | 459 ++- node/src/proxy_client/mod.rs | 12 +- node/src/proxy_client/stream_establisher.rs | 8 +- node/src/proxy_client/stream_handler_pool.rs | 155 +- node/src/proxy_client/stream_reader.rs | 26 +- .../client_request_payload_factory.rs | 243 +- node/src/proxy_server/http_protocol_pack.rs | 167 +- node/src/proxy_server/mod.rs | 2999 +++++++++-------- node/src/proxy_server/protocol_pack.rs | 21 +- .../proxy_server/server_impersonator_http.rs | 25 +- .../proxy_server/server_impersonator_tls.rs | 4 +- node/src/proxy_server/tls_protocol_pack.rs | 354 +- node/src/stream_handler_pool.rs | 62 +- node/src/stream_reader.rs | 38 +- node/src/sub_lib/dispatcher.rs | 22 +- node/src/sub_lib/hopper.rs | 11 +- node/src/sub_lib/host.rs | 39 + node/src/sub_lib/http_packet_framer.rs | 2 +- .../migrations/client_request_payload.rs | 14 +- node/src/sub_lib/mod.rs | 1 + node/src/sub_lib/neighborhood.rs | 54 +- node/src/sub_lib/proxy_server.rs | 14 +- node/src/sub_lib/route.rs | 121 +- node/src/sub_lib/sequence_buffer.rs | 8 +- node/src/sub_lib/stream_handler_pool.rs | 2 +- node/src/sub_lib/stream_key.rs | 15 +- node/src/sub_lib/ttl_hashmap.rs | 123 +- node/src/test_utils/mod.rs | 45 +- node/src/test_utils/recorder.rs | 6 +- 37 files changed, 3053 insertions(+), 2302 deletions(-) create mode 100644 node/src/sub_lib/host.rs diff --git a/.gitignore b/.gitignore index 88a2ab5f9..666a5a098 100644 --- a/.gitignore +++ b/.gitignore @@ -4,6 +4,7 @@ **/*.rs.bk .idea/azure/ .idea/inspectionProfiles/Project_Default.xml +.idea/copilot.data.migration.* ### Node node_modules diff --git a/multinode_integration_tests/tests/connection_termination_test.rs b/multinode_integration_tests/tests/connection_termination_test.rs index 2657661b8..287f2f120 100644 --- a/multinode_integration_tests/tests/connection_termination_test.rs +++ b/multinode_integration_tests/tests/connection_termination_test.rs @@ -1,6 +1,7 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use masq_lib::blockchains::chains::Chain; +use masq_lib::constants::HTTP_PORT; use masq_lib::test_utils::utils::TEST_DEFAULT_MULTINODE_CHAIN; use masq_lib::utils::find_free_port; use multinode_integration_tests_lib::masq_mock_node::MASQMockNode; @@ -86,13 +87,12 @@ fn reported_server_drop() { let (_, _, lcp) = mock_node .wait_for_package(&masquerader, Duration::from_secs(2)) .unwrap(); - let (stream_key, return_route_id) = - context_from_request_lcp(lcp, real_node.main_cryptde_null().unwrap(), &exit_cryptde); + let stream_key = stream_key_from_request_lcp(lcp, &exit_cryptde); mock_node .transmit_package( mock_node.port_list()[0], - create_server_drop_report(&mock_node, &real_node, stream_key, return_route_id), + create_server_drop_report(&mock_node, &real_node, stream_key), &masquerader, real_node.main_public_key(), real_node.socket_addr(PortSelector::First), @@ -115,7 +115,7 @@ fn actual_server_drop() { let server_port = find_free_port(); let mut server = real_node.make_server(server_port); let masquerader = JsonMasquerader::new(); - let (stream_key, return_route_id) = arbitrary_context(); + let stream_key = arbitrary_stream_key(); let index: u64 = 0; request_server_payload( index, @@ -125,7 +125,6 @@ fn actual_server_drop() { &mut server, &masquerader, stream_key, - return_route_id, ); let index: u64 = 1; request_server_payload( @@ -136,7 +135,6 @@ fn actual_server_drop() { &mut server, &masquerader, stream_key, - return_route_id, ); server.shutdown(); @@ -174,7 +172,6 @@ fn request_server_payload( server: &mut MASQNodeServer, masquerader: &JsonMasquerader, stream_key: StreamKey, - return_route_id: u32, ) { mock_node .transmit_package( @@ -184,7 +181,6 @@ fn request_server_payload( &mock_node, &real_node, stream_key, - return_route_id, &server, cluster.chain, ), @@ -212,7 +208,7 @@ fn reported_client_drop() { let server_port = find_free_port(); let mut server = real_node.make_server(server_port); let masquerader = JsonMasquerader::new(); - let (stream_key, return_route_id) = arbitrary_context(); + let stream_key = arbitrary_stream_key(); let index: u64 = 0; mock_node .transmit_package( @@ -222,7 +218,6 @@ fn reported_client_drop() { &mock_node, &real_node, stream_key, - return_route_id, &server, cluster.chain, ), @@ -240,7 +235,7 @@ fn reported_client_drop() { mock_node .transmit_package( mock_node.port_list()[0], - create_client_drop_report(&mock_node, &real_node, stream_key, return_route_id), + create_client_drop_report(&mock_node, &real_node, stream_key), &masquerader, real_node.main_public_key(), real_node.socket_addr(PortSelector::First), @@ -322,11 +317,7 @@ fn full_neighbor(one: &mut NodeRecord, another: &mut NodeRecord) { .unwrap(); } -fn context_from_request_lcp( - lcp: LiveCoresPackage, - originating_cryptde: &dyn CryptDE, - exit_cryptde: &dyn CryptDE, -) -> (StreamKey, u32) { +fn stream_key_from_request_lcp(lcp: LiveCoresPackage, exit_cryptde: &dyn CryptDE) -> StreamKey { let payload = match decodex::(exit_cryptde, &lcp.payload).unwrap() { MessageType::ClientRequest(vd) => vd .extract(&node_lib::sub_lib::migrations::client_request_payload::MIGRATIONS) @@ -334,15 +325,11 @@ fn context_from_request_lcp( mt => panic!("Unexpected: {:?}", mt), }; let stream_key = payload.stream_key; - let return_route_id = decodex::(originating_cryptde, &lcp.route.hops[6]).unwrap(); - (stream_key, return_route_id) + stream_key } -fn arbitrary_context() -> (StreamKey, u32) { - ( - StreamKey::make_meaningful_stream_key("arbitrary_context"), - 12345678, - ) +fn arbitrary_stream_key() -> StreamKey { + StreamKey::make_meaningful_stream_key("arbitrary_context") } fn create_request_icp( @@ -350,12 +337,12 @@ fn create_request_icp( originating_node: &MASQMockNode, exit_node: &MASQRealNode, stream_key: StreamKey, - return_route_id: u32, server: &MASQNodeServer, chain: Chain, ) -> IncipientCoresPackage { + let originating_main_cryptde = originating_node.main_cryptde_null().unwrap(); IncipientCoresPackage::new( - originating_node.main_cryptde_null().unwrap(), + originating_main_cryptde, Route::round_trip( RouteSegment::new( vec![ @@ -371,9 +358,8 @@ fn create_request_icp( ], Component::ProxyServer, ), - originating_node.main_cryptde_null().unwrap(), + originating_main_cryptde, originating_node.consuming_wallet(), - return_route_id, Some(chain.rec().contract), ) .unwrap(), @@ -382,7 +368,7 @@ fn create_request_icp( &ClientRequestPayload_0v1 { stream_key, sequenced_packet: SequencedPacket::new(Vec::from(HTTP_REQUEST), index, false), - target_hostname: Some(format!("{}", server.local_addr().ip())), + target_hostname: format!("{}", server.local_addr().ip()), target_port: server.local_addr().port(), protocol: ProxyProtocol::HTTP, originator_public_key: originating_node.main_public_key().clone(), @@ -400,8 +386,9 @@ fn create_meaningless_icp( let socket_addr = SocketAddr::from_str("3.2.1.0:7654").unwrap(); let stream_key = StreamKey::make_meaningful_stream_key("Chancellor on brink of second bailout for banks"); + let main_cryptde = originating_node.main_cryptde_null().unwrap(); IncipientCoresPackage::new( - originating_node.main_cryptde_null().unwrap(), + main_cryptde, Route::round_trip( RouteSegment::new( vec![ @@ -417,9 +404,8 @@ fn create_meaningless_icp( ], Component::ProxyServer, ), - originating_node.main_cryptde_null().unwrap(), + main_cryptde, originating_node.consuming_wallet(), - 1357, Some(TEST_DEFAULT_MULTINODE_CHAIN.rec().contract), ) .unwrap(), @@ -428,7 +414,7 @@ fn create_meaningless_icp( &ClientRequestPayload_0v1 { stream_key, sequenced_packet: SequencedPacket::new(Vec::from(HTTP_REQUEST), 0, false), - target_hostname: Some(format!("nowhere.com")), + target_hostname: "nowhere.com".to_string(), target_port: socket_addr.port(), protocol: ProxyProtocol::HTTP, originator_public_key: originating_node.main_public_key().clone(), @@ -443,8 +429,9 @@ fn create_server_drop_report( exit_node: &MASQMockNode, originating_node: &MASQRealNode, stream_key: StreamKey, - return_route_id: u32, ) -> IncipientCoresPackage { + let exit_main_cryptde = exit_node.main_cryptde_null().unwrap(); + let originating_main_cryptde = originating_node.main_cryptde_null().unwrap(); let mut route = Route::round_trip( RouteSegment::new( vec![ @@ -460,15 +447,12 @@ fn create_server_drop_report( ], Component::ProxyServer, ), - originating_node.main_cryptde_null().unwrap(), + originating_main_cryptde, originating_node.consuming_wallet(), - return_route_id, Some(TEST_DEFAULT_MULTINODE_CHAIN.rec().contract), ) .unwrap(); - route - .shift(originating_node.main_cryptde_null().unwrap()) - .unwrap(); + route.shift(originating_main_cryptde).unwrap(); let payload = MessageType::ClientResponse(VersionedData::new( &node_lib::sub_lib::migrations::client_response_payload::MIGRATIONS, &ClientResponsePayload_0v1 { @@ -478,7 +462,7 @@ fn create_server_drop_report( )); IncipientCoresPackage::new( - exit_node.main_cryptde_null().unwrap(), + exit_main_cryptde, route, payload, originating_node.alias_public_key(), @@ -490,8 +474,8 @@ fn create_client_drop_report( originating_node: &MASQMockNode, exit_node: &MASQRealNode, stream_key: StreamKey, - return_route_id: u32, ) -> IncipientCoresPackage { + let originating_main_cryptde = originating_node.main_cryptde_null().unwrap(); let route = Route::round_trip( RouteSegment::new( vec![ @@ -507,9 +491,8 @@ fn create_client_drop_report( ], Component::ProxyServer, ), - originating_node.main_cryptde_null().unwrap(), + originating_main_cryptde, originating_node.consuming_wallet(), - return_route_id, Some(TEST_DEFAULT_MULTINODE_CHAIN.rec().contract), ) .unwrap(); @@ -518,15 +501,15 @@ fn create_client_drop_report( &ClientRequestPayload_0v1 { stream_key, sequenced_packet: SequencedPacket::new(vec![], 1, true), - target_hostname: Some(String::from("doesnt.matter.com")), - target_port: 80, + target_hostname: String::from("doesnt.matter.com"), + target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: originating_node.main_public_key().clone(), }, )); IncipientCoresPackage::new( - originating_node.main_cryptde_null().unwrap(), + originating_main_cryptde, route, payload, exit_node.main_public_key(), diff --git a/multinode_integration_tests/tests/self_test.rs b/multinode_integration_tests/tests/self_test.rs index 973affaf0..c19db4c33 100644 --- a/multinode_integration_tests/tests/self_test.rs +++ b/multinode_integration_tests/tests/self_test.rs @@ -16,6 +16,7 @@ use node_lib::sub_lib::dispatcher::Component; use node_lib::sub_lib::hopper::IncipientCoresPackage; use node_lib::sub_lib::route::Route; use node_lib::sub_lib::route::RouteSegment; +use node_lib::sub_lib::stream_key::StreamKey; use node_lib::test_utils::{make_meaningless_message_type, make_paying_wallet}; use std::collections::HashSet; use std::io::ErrorKind; @@ -68,6 +69,7 @@ fn server_relays_cores_package() { let masquerader = JsonMasquerader::new(); let server = MASQCoresServer::new(cluster.chain); let cryptde = server.main_cryptde(); + let stream_key = StreamKey::make_meaningless_stream_key(); let mut client = MASQCoresClient::new(server.local_addr(), cryptde); let mut route = Route::one_way( RouteSegment::new( @@ -82,7 +84,7 @@ fn server_relays_cores_package() { let incipient = IncipientCoresPackage::new( cryptde, route.clone(), - make_meaningless_message_type(), + make_meaningless_message_type(stream_key), &cryptde.public_key(), ) .unwrap(); @@ -99,7 +101,7 @@ fn server_relays_cores_package() { route.shift(cryptde).unwrap(); assert_eq!(expired.remaining_route, route); - assert_eq!(expired.payload, make_meaningless_message_type()); + assert_eq!(expired.payload, make_meaningless_message_type(stream_key)); } #[test] @@ -111,6 +113,7 @@ fn one_mock_node_talks_to_another() { let mock_node_1 = cluster.get_mock_node_by_name("mock_node_1").unwrap(); let mock_node_2 = cluster.get_mock_node_by_name("mock_node_2").unwrap(); let cryptde = CryptDENull::new(TEST_DEFAULT_CHAIN); + let stream_key = StreamKey::make_meaningless_stream_key(); let route = Route::one_way( RouteSegment::new( vec![ @@ -127,7 +130,7 @@ fn one_mock_node_talks_to_another() { let incipient_cores_package = IncipientCoresPackage::new( &cryptde, route, - make_meaningless_message_type(), + make_meaningless_message_type(stream_key), &mock_node_2.main_public_key(), ) .unwrap(); @@ -156,7 +159,7 @@ fn one_mock_node_talks_to_another() { assert_eq!(package_to, mock_node_2.socket_addr(PortSelector::First)); assert_eq!( expired_cores_package.payload, - make_meaningless_message_type() + make_meaningless_message_type(stream_key) ); } diff --git a/node/src/dispatcher.rs b/node/src/dispatcher.rs index ac698e043..4b01cd593 100644 --- a/node/src/dispatcher.rs +++ b/node/src/dispatcher.rs @@ -271,13 +271,13 @@ mod tests { let recording_arc = proxy_server.get_recording(); let awaiter = proxy_server.get_awaiter(); let client_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); - let reception_port = Some(8080); + let reception_port_opt = Some(8080); let data: Vec = vec![9, 10, 11]; let ibcd_in = InboundClientData { timestamp: SystemTime::now(), client_addr, - reception_port, - sequence_number: Some(0), + reception_port_opt, + sequence_number_opt: Some(0), last_data: false, is_clandestine: false, data: data.clone(), @@ -310,15 +310,15 @@ mod tests { let subject_addr = subject.start(); let (hopper, hopper_awaiter, hopper_recording_arc) = make_recorder(); let client_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); - let reception_port = Some(8080); + let reception_port_opt = Some(8080); let data: Vec = vec![9, 10, 11]; let ibcd_in = InboundClientData { timestamp: SystemTime::now(), client_addr, - reception_port, + reception_port_opt, last_data: false, is_clandestine: true, - sequence_number: None, + sequence_number_opt: None, data: data.clone(), }; let mut peer_actors = peer_actors_builder().hopper(hopper).build(); @@ -350,15 +350,15 @@ mod tests { let subject_addr = subject.start(); let subject_ibcd = subject_addr.recipient::(); let client_addr = SocketAddr::from_str("1.2.3.4:8765").unwrap(); - let reception_port = Some(1234); + let reception_port_opt = Some(1234); let data: Vec = vec![9, 10, 11]; let ibcd_in = InboundClientData { timestamp: SystemTime::now(), client_addr, - reception_port, + reception_port_opt, last_data: false, is_clandestine: false, - sequence_number: Some(0), + sequence_number_opt: Some(0), data: data.clone(), }; @@ -376,15 +376,15 @@ mod tests { let subject_addr = subject.start(); let subject_ibcd = subject_addr.recipient::(); let client_addr = SocketAddr::from_str("1.2.3.4:8765").unwrap(); - let reception_port = Some(1234); + let reception_port_opt = Some(1234); let data: Vec = vec![9, 10, 11]; let ibcd_in = InboundClientData { timestamp: SystemTime::now(), client_addr, - reception_port, + reception_port_opt, last_data: false, is_clandestine: true, - sequence_number: None, + sequence_number_opt: None, data: data.clone(), }; @@ -406,7 +406,7 @@ mod tests { let obcd = TransmitDataMsg { endpoint: Endpoint::Socket(socket_addr), last_data: false, - sequence_number: Some(0), + sequence_number_opt: Some(0), data: data.clone(), }; @@ -430,7 +430,7 @@ mod tests { let obcd = TransmitDataMsg { endpoint: Endpoint::Socket(socket_addr), last_data: false, - sequence_number: None, + sequence_number_opt: None, data: data.clone(), }; let mut peer_actors = peer_actors_builder().build(); diff --git a/node/src/hopper/consuming_service.rs b/node/src/hopper/consuming_service.rs index 7d5660a8b..0eb34cb52 100644 --- a/node/src/hopper/consuming_service.rs +++ b/node/src/hopper/consuming_service.rs @@ -100,10 +100,10 @@ impl ConsumingService { let ibcd = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 0), - reception_port: None, + reception_port_opt: None, last_data: false, is_clandestine: true, - sequence_number: None, + sequence_number_opt: None, data: encrypted_package.into(), }; debug!( @@ -119,7 +119,7 @@ impl ConsumingService { endpoint: next_stop, last_data: false, // Hopper-to-Hopper clandestine streams are never remotely killed data: encrypted_package.into(), - sequence_number: None, + sequence_number_opt: None, }; debug!( @@ -143,6 +143,7 @@ mod tests { use crate::sub_lib::node_addr::NodeAddr; use crate::sub_lib::route::Route; use crate::sub_lib::route::RouteSegment; + use crate::sub_lib::stream_key::StreamKey; use crate::test_utils::recorder::make_recorder; use crate::test_utils::recorder::peer_actors_builder; use crate::test_utils::{make_meaningless_message_type, make_paying_wallet}; @@ -168,7 +169,7 @@ mod tests { CRYPTDE_PAIR.main.as_ref(), &target_key, &target_node_addr, - make_meaningless_message_type(), + make_meaningless_message_type(StreamKey::make_meaningless_stream_key()), ) .unwrap(); let system = System::new(""); @@ -192,7 +193,7 @@ mod tests { &TransmitDataMsg { endpoint: Endpoint::Socket(SocketAddr::from_str("1.2.1.2:1212").unwrap()), last_data: false, - sequence_number: None, + sequence_number_opt: None, data: encodex(CRYPTDE_PAIR.main.as_ref(), &target_key, &lcp) .unwrap() .into(), @@ -242,7 +243,7 @@ mod tests { Some(TEST_DEFAULT_CHAIN.rec().contract), ) .unwrap(); - let payload = make_meaningless_message_type(); + let payload = make_meaningless_message_type(StreamKey::make_meaningless_stream_key()); let incipient_cores_package = IncipientCoresPackage::new(cryptde, route.clone(), payload, &destination_key).unwrap(); let system = System::new("converts_incipient_message_to_live_and_sends_to_dispatcher"); @@ -266,7 +267,7 @@ mod tests { TransmitDataMsg { endpoint: Endpoint::Key(destination_key.clone()), last_data: false, - sequence_number: None, + sequence_number_opt: None, data: expected_lcp_enc.into(), }, *record, @@ -289,7 +290,7 @@ mod tests { Some(TEST_DEFAULT_CHAIN.rec().contract), ) .unwrap(); - let payload = make_meaningless_message_type(); + let payload = make_meaningless_message_type(StreamKey::make_meaningless_stream_key()); let incipient_cores_package = IncipientCoresPackage::new(cryptde, route.clone(), payload, &destination_key).unwrap(); let system = System::new("consume_sends_zero_hop_incipient_directly_to_hopper"); @@ -317,10 +318,10 @@ mod tests { InboundClientData { timestamp: record.timestamp, client_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 0), - reception_port: None, + reception_port_opt: None, last_data: false, is_clandestine: true, - sequence_number: None, + sequence_number_opt: None, data: expected_lcp_enc.into(), }, ); @@ -340,7 +341,7 @@ mod tests { IncipientCoresPackage::new( CRYPTDE_PAIR.main.as_ref(), Route { hops: vec![] }, - make_meaningless_message_type(), + make_meaningless_message_type(StreamKey::make_meaningless_stream_key()), &PublicKey::new(&[1, 2]), ) .unwrap(), diff --git a/node/src/hopper/live_cores_package.rs b/node/src/hopper/live_cores_package.rs index 2b05b27e8..cac27bebc 100644 --- a/node/src/hopper/live_cores_package.rs +++ b/node/src/hopper/live_cores_package.rs @@ -100,6 +100,7 @@ mod tests { use crate::sub_lib::node_addr::NodeAddr; use crate::sub_lib::route::RouteSegment; use crate::sub_lib::route::{Route, RouteError}; + use crate::sub_lib::stream_key::StreamKey; use crate::test_utils::{ make_meaningless_message_type, make_meaningless_route, make_paying_wallet, }; @@ -141,7 +142,9 @@ mod tests { let relay_key = PublicKey::new(&[1, 2]); let relay_cryptde = CryptDENull::from(&relay_key, TEST_DEFAULT_CHAIN); let cryptde = CRYPTDE_PAIR.main.as_ref(); - let serialized_payload = serde_cbor::ser::to_vec(&make_meaningless_message_type()).unwrap(); + let stream_key = StreamKey::make_meaningless_stream_key(); + let serialized_payload = + serde_cbor::ser::to_vec(&make_meaningless_message_type(stream_key)).unwrap(); let encrypted_payload = cryptde .encode(&destination_key, &PlainData::new(&serialized_payload)) .unwrap(); @@ -204,7 +207,7 @@ mod tests { let key34 = PublicKey::new(&[3, 4]); let node_addr34 = NodeAddr::new(&IpAddr::from_str("3.4.3.4").unwrap(), &[1234]); let mut route = Route::single_hop(&key34, cryptde).unwrap(); - let payload = make_meaningless_message_type(); + let payload = make_meaningless_message_type(StreamKey::make_meaningless_stream_key()); let incipient = NoLookupIncipientCoresPackage::new(cryptde, &key34, &node_addr34, payload.clone()) @@ -228,7 +231,7 @@ mod tests { cryptde, &blank_key, &node_addr34, - make_meaningless_message_type(), + make_meaningless_message_type(StreamKey::make_meaningless_stream_key()), ); assert_eq!( @@ -254,7 +257,7 @@ mod tests { Some(contract_address), ) .unwrap(); - let payload = make_meaningless_message_type(); + let payload = make_meaningless_message_type(StreamKey::make_meaningless_stream_key()); let incipient = IncipientCoresPackage::new(cryptde, route.clone(), payload.clone(), &key56).unwrap(); @@ -280,7 +283,7 @@ mod tests { let incipient = IncipientCoresPackage::new( cryptde, Route { hops: vec![] }, - make_meaningless_message_type(), + make_meaningless_message_type(StreamKey::make_meaningless_stream_key()), &PublicKey::new(&[3, 4]), ) .unwrap(); @@ -297,7 +300,8 @@ mod tests { #[test] fn expired_cores_package_can_be_constructed_from_live_cores_package() { let immediate_neighbor_ip = SocketAddr::from_str("1.2.3.4:1234").unwrap(); - let payload = make_meaningless_message_type(); + let stream_key = StreamKey::make_meaningless_stream_key(); + let payload = make_meaningless_message_type(stream_key); let first_stop_key = PublicKey::new(&[3, 4]); let first_stop_cryptde = CryptDENull::from(&first_stop_key, TEST_DEFAULT_CHAIN); let relay_key = PublicKey::new(&[1, 2]); @@ -316,7 +320,6 @@ mod tests { ), cryptde, Some(paying_wallet.clone()), - 1234, Some(contract_address), ) .unwrap(); @@ -375,11 +378,6 @@ mod tests { Component::ProxyServer, ) ); - assert_eq!( - route.hops[0], - crate::test_utils::encrypt_return_route_id(1234, cryptde), - ); - route.hops.remove(0); assert_eq!( &route.hops[0].as_slice()[..8], &[52, 52, 52, 52, 52, 52, 52, 52] diff --git a/node/src/hopper/mod.rs b/node/src/hopper/mod.rs index 588016440..b2f3d6dc0 100644 --- a/node/src/hopper/mod.rs +++ b/node/src/hopper/mod.rs @@ -149,6 +149,7 @@ mod tests { use crate::sub_lib::hopper::IncipientCoresPackage; use crate::sub_lib::route::Route; use crate::sub_lib::route::RouteSegment; + use crate::sub_lib::stream_key::StreamKey; use crate::test_utils::unshared_test_utils::prove_that_crash_request_handler_is_hooked_up; use crate::test_utils::{ make_meaningless_message_type, make_paying_wallet, route_to_proxy_client, @@ -175,8 +176,10 @@ mod tests { fn panics_if_routing_service_is_unbound() { let main_cryptde = CRYPTDE_PAIR.main.as_ref(); let client_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); - let route = route_to_proxy_client(&main_cryptde.public_key(), main_cryptde); - let serialized_payload = serde_cbor::ser::to_vec(&make_meaningless_message_type()).unwrap(); + let route = route_to_proxy_client(&main_cryptde.public_key(), main_cryptde, false); + let stream_key = StreamKey::make_meaningless_stream_key(); + let serialized_payload = + serde_cbor::ser::to_vec(&make_meaningless_message_type(stream_key)).unwrap(); let data = main_cryptde .encode( &main_cryptde.public_key(), @@ -193,10 +196,10 @@ mod tests { let inbound_client_data = InboundClientData { timestamp: SystemTime::now(), client_addr, - reception_port: None, + reception_port_opt: None, last_data: false, is_clandestine: false, - sequence_number: None, + sequence_number_opt: None, data: encrypted_package, }; let system = System::new("panics_if_routing_service_is_unbound"); @@ -234,7 +237,7 @@ mod tests { let incipient_package = IncipientCoresPackage::new( main_cryptde, route, - make_meaningless_message_type(), + make_meaningless_message_type(StreamKey::make_meaningless_stream_key()), &main_cryptde.public_key(), ) .unwrap(); diff --git a/node/src/hopper/routing_service.rs b/node/src/hopper/routing_service.rs index 7f7134d20..69a88aed4 100644 --- a/node/src/hopper/routing_service.rs +++ b/node/src/hopper/routing_service.rs @@ -186,10 +186,10 @@ impl RoutingService { let inbound_client_data = InboundClientData { timestamp: ibcd_but_data.timestamp, client_addr: ibcd_but_data.client_addr, - reception_port: ibcd_but_data.reception_port, + reception_port_opt: ibcd_but_data.reception_port_opt, last_data: ibcd_but_data.last_data, is_clandestine: ibcd_but_data.is_clandestine, - sequence_number: ibcd_but_data.sequence_number, + sequence_number_opt: ibcd_but_data.sequence_number_opt, data: payload.into(), }; self.routing_service_subs @@ -495,7 +495,7 @@ impl RoutingService { endpoint: Endpoint::Key(next_hop.public_key), last_data, data: next_live_package_enc.into(), - sequence_number: None, + sequence_number_opt: None, }) } } @@ -542,16 +542,18 @@ mod tests { #[test] fn dns_resolution_failures_are_reported_to_the_proxy_server() { - let cryptde_pair = CRYPTDE_PAIR.clone(); - let route = - route_to_proxy_server(&cryptde_pair.main.public_key(), cryptde_pair.main.as_ref()); + let route = route_to_proxy_server( + &CRYPTDE_PAIR.main.public_key(), + CRYPTDE_PAIR.main.as_ref(), + false, + ); let stream_key = StreamKey::make_meaningless_stream_key(); let dns_resolve_failure = DnsResolveFailure_0v1::new(stream_key); let lcp = LiveCoresPackage::new( route, encodex( - cryptde_pair.alias.as_ref(), - &cryptde_pair.alias.public_key(), + CRYPTDE_PAIR.alias.as_ref(), + &CRYPTDE_PAIR.alias.public_key(), &MessageType::DnsResolveFailed(VersionedData::new( &crate::sub_lib::migrations::dns_resolve_failure::MIGRATIONS, &dns_resolve_failure.clone(), @@ -560,16 +562,16 @@ mod tests { .unwrap(), ); let data_enc = encodex( - cryptde_pair.main.as_ref(), - &cryptde_pair.main.public_key(), + CRYPTDE_PAIR.main.as_ref(), + &CRYPTDE_PAIR.main.public_key(), &lcp, ) .unwrap(); let inbound_client_data = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port: None, - sequence_number: None, + reception_port_opt: None, + sequence_number_opt: None, last_data: false, is_clandestine: false, data: data_enc.into(), @@ -579,7 +581,7 @@ mod tests { let system = System::new("dns_resolution_failures_are_reported_to_the_proxy_server"); let peer_actors = peer_actors_builder().proxy_server(proxy_server).build(); let subject = RoutingService::new( - cryptde_pair, + CRYPTDE_PAIR.clone(), RoutingServiceSubs { proxy_client_subs_opt: peer_actors.proxy_client_opt, proxy_server_subs: peer_actors.proxy_server, @@ -606,36 +608,38 @@ mod tests { #[test] fn logs_and_ignores_message_that_cannot_be_deserialized() { init_test_logging(); - let cryptde_pair = CRYPTDE_PAIR.clone(); - let route = - route_from_proxy_client(&cryptde_pair.main.public_key(), cryptde_pair.main.as_ref()); + let route = route_from_proxy_client( + &CRYPTDE_PAIR.main.public_key(), + CRYPTDE_PAIR.main.as_ref(), + false, + ); let lcp = LiveCoresPackage::new( route, encodex( - cryptde_pair.main.as_ref(), - &cryptde_pair.main.public_key(), + CRYPTDE_PAIR.main.as_ref(), + &CRYPTDE_PAIR.main.public_key(), &[42u8], ) .unwrap(), ); let data_enc = encodex( - cryptde_pair.main.as_ref(), - &cryptde_pair.main.public_key(), + CRYPTDE_PAIR.main.as_ref(), + &CRYPTDE_PAIR.main.public_key(), &lcp, ) .unwrap(); let inbound_client_data = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port: None, - sequence_number: None, + reception_port_opt: None, + sequence_number_opt: None, last_data: false, is_clandestine: false, data: data_enc.into(), }; let peer_actors = peer_actors_builder().build(); let subject = RoutingService::new( - cryptde_pair, + CRYPTDE_PAIR.clone(), RoutingServiceSubs { proxy_client_subs_opt: peer_actors.proxy_client_opt, proxy_server_subs: peer_actors.proxy_server, @@ -661,7 +665,7 @@ mod tests { init_test_logging(); let main_cryptde = CryptDEReal::new(TEST_DEFAULT_CHAIN); let rogue_cryptde = CryptDEReal::new(TEST_DEFAULT_CHAIN); - let route = route_from_proxy_client(main_cryptde.public_key(), &main_cryptde); + let route = route_from_proxy_client(main_cryptde.public_key(), &main_cryptde, false); let lcp = LiveCoresPackage::new( route, encodex(&rogue_cryptde, rogue_cryptde.public_key(), &[42u8]).unwrap(), @@ -670,8 +674,8 @@ mod tests { let inbound_client_data = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port: None, - sequence_number: None, + reception_port_opt: None, + sequence_number_opt: None, last_data: false, is_clandestine: false, data: data_enc.into(), @@ -707,7 +711,7 @@ mod tests { fn logs_and_ignores_message_that_had_invalid_destination() { init_test_logging(); let main_cryptde = CRYPTDE_PAIR.main.as_ref(); - let route = route_from_proxy_client(&main_cryptde.public_key(), main_cryptde); + let route = route_from_proxy_client(&main_cryptde.public_key(), main_cryptde, false); let payload = GossipBuilder::empty(); let lcp = LiveCoresPackage::new( route, @@ -722,8 +726,8 @@ mod tests { let inbound_client_data = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port: None, - sequence_number: None, + reception_port_opt: None, + sequence_number_opt: None, last_data: false, is_clandestine: false, data: data_enc.into(), @@ -753,7 +757,7 @@ mod tests { BAN_CACHE.clear(); let main_cryptde = CRYPTDE_PAIR.main.as_ref(); let (component, _, component_recording_arc) = make_recorder(); - let route = route_to_proxy_client(&main_cryptde.public_key(), main_cryptde); + let route = route_to_proxy_client(&main_cryptde.public_key(), main_cryptde, false); let payload = make_request_payload(0, main_cryptde); let lcp = LiveCoresPackage::new( route, @@ -772,8 +776,8 @@ mod tests { let inbound_client_data = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port: None, - sequence_number: None, + reception_port_opt: None, + sequence_number_opt: None, last_data: true, is_clandestine: false, data: data_enc.into(), @@ -823,7 +827,7 @@ mod tests { init_test_logging(); BAN_CACHE.clear(); let main_cryptde = CRYPTDE_PAIR.main.as_ref(); - let route = route_to_proxy_client(&main_cryptde.public_key(), main_cryptde); + let route = route_to_proxy_client(&main_cryptde.public_key(), main_cryptde, false); let payload = make_request_payload(0, main_cryptde); let lcp = LiveCoresPackage::new( route, @@ -841,8 +845,8 @@ mod tests { let inbound_client_data = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port: None, - sequence_number: None, + reception_port_opt: None, + sequence_number_opt: None, last_data: true, is_clandestine: false, data: data_enc.into(), @@ -881,7 +885,7 @@ mod tests { let main_cryptde = CRYPTDE_PAIR.main.as_ref(); let alias_cryptde = CRYPTDE_PAIR.alias.as_ref(); let (proxy_server, _, proxy_server_recording_arc) = make_recorder(); - let route = route_to_proxy_server(&main_cryptde.public_key(), main_cryptde); + let route = route_to_proxy_server(&main_cryptde.public_key(), main_cryptde, false); let payload = make_response_payload(0); let lcp = LiveCoresPackage::new( route, @@ -897,10 +901,10 @@ mod tests { let inbound_client_data = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.3.2.4:5678").unwrap(), - reception_port: None, + reception_port_opt: None, last_data: false, is_clandestine: true, - sequence_number: None, + sequence_number_opt: None, data: lcp_enc.into(), }; @@ -974,10 +978,10 @@ mod tests { let inbound_client_data = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.3.2.4:5678").unwrap(), - reception_port: None, + reception_port_opt: None, last_data: false, is_clandestine: true, - sequence_number: None, + sequence_number_opt: None, data: data_enc.into(), }; @@ -1047,10 +1051,10 @@ mod tests { let inbound_client_data = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.3.2.4:5678").unwrap(), - reception_port: None, + reception_port_opt: None, last_data: false, is_clandestine: true, - sequence_number: None, + sequence_number_opt: None, data: data_enc.into(), }; @@ -1123,10 +1127,10 @@ mod tests { let inbound_client_data = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port: None, + reception_port_opt: None, last_data: true, is_clandestine: true, - sequence_number: None, + sequence_number_opt: None, data: data_enc.into(), }; @@ -1166,7 +1170,7 @@ mod tests { TransmitDataMsg { endpoint: Endpoint::Key(next_key.clone()), last_data: true, - sequence_number: None, + sequence_number_opt: None, data: expected_lcp_enc.into(), } ); @@ -1215,10 +1219,10 @@ mod tests { let inbound_client_data = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port: None, + reception_port_opt: None, last_data: true, is_clandestine: true, - sequence_number: None, + sequence_number_opt: None, data: data_enc.into(), }; @@ -1258,10 +1262,10 @@ mod tests { InboundClientData { timestamp: record.timestamp, client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port: None, + reception_port_opt: None, last_data: true, is_clandestine: true, - sequence_number: None, + sequence_number_opt: None, data: expected_lcp_enc.into() } ); @@ -1276,7 +1280,7 @@ mod tests { let origin_key = PublicKey::new(&[1, 2]); let origin_cryptde = CryptDENull::from(&origin_key, TEST_DEFAULT_CHAIN); let destination_key = PublicKey::new(&[3, 4]); - let payload = make_meaningless_message_type(); + let payload = make_meaningless_message_type(StreamKey::make_meaningless_stream_key()); let route = Route::one_way( RouteSegment::new( vec![&origin_key, &main_cryptde.public_key(), &destination_key], @@ -1297,10 +1301,10 @@ mod tests { let inbound_client_data = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port: None, + reception_port_opt: None, last_data: true, is_clandestine: true, - sequence_number: None, + sequence_number_opt: None, data: data_enc.into(), }; let system = System::new( @@ -1394,10 +1398,10 @@ mod tests { let inbound_client_data = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port: None, + reception_port_opt: None, last_data: true, is_clandestine: true, - sequence_number: None, + sequence_number_opt: None, data: data_enc.into(), }; let system = System::new( @@ -1570,10 +1574,10 @@ mod tests { let inbound_client_data = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port: None, + reception_port_opt: None, last_data: true, is_clandestine: true, - sequence_number: None, + sequence_number_opt: None, data: data_enc.into(), }; let system = System::new("test"); @@ -1640,10 +1644,10 @@ mod tests { let inbound_client_data = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port: None, + reception_port_opt: None, last_data: true, is_clandestine: true, - sequence_number: None, + sequence_number_opt: None, data: data_enc.into(), }; let system = System::new("test"); @@ -1684,10 +1688,10 @@ mod tests { let inbound_client_data = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port: None, + reception_port_opt: None, last_data: true, is_clandestine: true, - sequence_number: None, + sequence_number_opt: None, data: vec![], }; let system = System::new("consume_logs_error_when_given_bad_input_data"); @@ -1741,10 +1745,10 @@ mod tests { let inbound_client_data = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port: None, + reception_port_opt: None, last_data: true, is_clandestine: true, - sequence_number: None, + sequence_number_opt: None, data: data_enc.into(), }; let system = System::new("consume_logs_error_when_given_bad_input_data"); @@ -1808,10 +1812,10 @@ mod tests { let ibcd = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port: None, + reception_port_opt: None, last_data: true, is_clandestine: true, - sequence_number: None, + sequence_number_opt: None, data: vec![], }; @@ -1873,7 +1877,7 @@ mod tests { &ClientRequestPayload_0v1 { stream_key: StreamKey::make_meaningless_stream_key(), sequenced_packet: SequencedPacket::new(vec![1, 2, 3, 4], 1234, false), - target_hostname: Some("hostname".to_string()), + target_hostname: "hostname".to_string(), target_port: 1234, protocol: ProxyProtocol::TLS, originator_public_key: PublicKey::new(b"1234"), diff --git a/node/src/neighborhood/mod.rs b/node/src/neighborhood/mod.rs index 9c685c0af..2345342d8 100644 --- a/node/src/neighborhood/mod.rs +++ b/node/src/neighborhood/mod.rs @@ -27,12 +27,15 @@ use crate::sub_lib::cryptde::PublicKey; use crate::sub_lib::dispatcher::{Component, StreamShutdownMsg}; use crate::sub_lib::hopper::{ExpiredCoresPackage, NoLookupIncipientCoresPackage}; use crate::sub_lib::hopper::{IncipientCoresPackage, MessageType}; +use crate::sub_lib::host::Host; +use crate::sub_lib::neighborhood::ConnectionProgressEvent; +use crate::sub_lib::neighborhood::ExpectedService::{Exit, Nothing, Routing}; +use crate::sub_lib::neighborhood::ExpectedServices::RoundTrip; use crate::sub_lib::neighborhood::RouteQueryResponse; use crate::sub_lib::neighborhood::UpdateNodeRecordMetadataMessage; use crate::sub_lib::neighborhood::{AskAboutDebutGossipMessage, NodeDescriptor}; use crate::sub_lib::neighborhood::{ConfigChange, RemoveNeighborMessage}; use crate::sub_lib::neighborhood::{ConfigChangeMsg, RouteQueryMessage}; -use crate::sub_lib::neighborhood::{ConnectionProgressEvent, ExpectedServices}; use crate::sub_lib::neighborhood::{ConnectionProgressMessage, ExpectedService}; use crate::sub_lib::neighborhood::{DispatcherNodeQueryMessage, GossipFailure_0v1}; use crate::sub_lib::neighborhood::{Hops, NeighborhoodMetadata, NodeQueryResponseMetadata}; @@ -105,7 +108,6 @@ pub struct Neighborhood { mode: NeighborhoodModeLight, min_hops: Hops, db_patch_size: u8, - next_return_route_id: u32, overall_connection_status: OverallConnectionStatus, chain: Chain, crashable: bool, @@ -430,7 +432,6 @@ impl Neighborhood { mode, min_hops, db_patch_size, - next_return_route_id: 0, overall_connection_status, chain: config.blockchain_bridge_config.chain, crashable: config.crash_point == CrashPoint::Message, @@ -496,9 +497,9 @@ impl Neighborhood { } fn handle_route_query_message(&mut self, msg: RouteQueryMessage) -> Option { - let debug_msg_opt = self.logger.debug_enabled().then(|| format!("{:?}", msg)); + let debug_msg_opt = self.logger.debug_enabled().then(|| format!("{}", msg.host)); let route_result = if self.mode == NeighborhoodModeLight::ZeroHop { - Ok(self.zero_hop_route_response()) + Ok(self.zero_hop_route_response(msg.host)) } else { self.make_round_trip_route(msg) }; @@ -882,7 +883,7 @@ impl Neighborhood { target_component: Component::ProxyClient, return_component_opt: Some(Component::ProxyServer), payload_size: 10000, - hostname_opt: None, + host: Host::new("booga.com", 1234), }; if self.handle_route_query_message(msg).is_some() { debug!( @@ -982,8 +983,7 @@ impl Neighborhood { .expect("route creation error") } - fn zero_hop_route_response(&mut self) -> RouteQueryResponse { - let return_route_id = self.advance_return_route_id(); + fn zero_hop_route_response(&mut self, host: Host) -> RouteQueryResponse { let route = Route::round_trip( RouteSegment::new( vec![self.cryptde.public_key(), self.cryptde.public_key()], @@ -995,17 +995,13 @@ impl Neighborhood { ), self.cryptde.as_ref(), None, - return_route_id, None, ) .expect("Couldn't create route"); RouteQueryResponse { route, - expected_services: ExpectedServices::RoundTrip( - vec![ExpectedService::Nothing, ExpectedService::Nothing], - vec![ExpectedService::Nothing, ExpectedService::Nothing], - return_route_id, - ), + expected_services: RoundTrip(vec![Nothing, Nothing], vec![Nothing, Nothing]), + host, } } @@ -1013,7 +1009,7 @@ impl Neighborhood { &mut self, request_msg: RouteQueryMessage, ) -> Result { - let hostname_opt = request_msg.hostname_opt.as_deref(); + let host = request_msg.host; let over = self.make_route_segment( self.cryptde.public_key(), request_msg.target_key_opt.as_ref(), @@ -1021,7 +1017,7 @@ impl Neighborhood { request_msg.target_component, request_msg.payload_size, RouteDirection::Over, - hostname_opt, + &host.name, )?; debug!(self.logger, "Route over: {:?}", over); // Estimate for routing-undesirability calculations. @@ -1038,16 +1034,17 @@ impl Neighborhood { .expect("No return component"), anticipated_response_payload_len, RouteDirection::Back, - hostname_opt, + &host.name, )?; debug!(self.logger, "Route back: {:?}", back); - self.compose_route_query_response(over, back) + self.compose_route_query_response(over, back, host) } fn compose_route_query_response( &mut self, over: RouteSegment, back: RouteSegment, + host: Host, ) -> Result { let segments = vec![&over, &back]; @@ -1070,22 +1067,17 @@ impl Neighborhood { Err(e) => return Err(e), }; - let return_route_id = self.advance_return_route_id(); Ok(RouteQueryResponse { route: Route::round_trip( over, back, self.cryptde.as_ref(), self.consuming_wallet_opt.clone(), - return_route_id, Some(self.chain.rec().contract), ) .expect("Internal error: bad route"), - expected_services: ExpectedServices::RoundTrip( - expected_request_services, - expected_response_services, - return_route_id, - ), + expected_services: RoundTrip(expected_request_services, expected_response_services), + host, }) } @@ -1098,7 +1090,7 @@ impl Neighborhood { target_component: Component, payload_size: usize, direction: RouteDirection, - hostname_opt: Option<&str>, + hostname: &str, ) -> Result { let route_opt = self.find_best_route_segment( origin, @@ -1106,7 +1098,7 @@ impl Neighborhood { minimum_hop_count, payload_size, direction, - hostname_opt, + hostname, ); match route_opt { None => { @@ -1145,20 +1137,20 @@ impl Neighborhood { match self.neighborhood_database.node_by_key(route_segment_key) { Some(node) => { if route_segment_key == self.neighborhood_database.root().public_key() { - Ok(ExpectedService::Nothing) + Ok(Nothing) } else { match (originator_key, exit_key) { (Some(originator_key), Some(exit_key)) if route_segment_key == originator_key || route_segment_key == exit_key => { - Ok(ExpectedService::Exit( + Ok(Exit( route_segment_key.clone(), node.earning_wallet(), *node.rate_pack(), )) } - (Some(_), Some(_)) => Ok(ExpectedService::Routing( + (Some(_), Some(_)) => Ok(Routing( route_segment_key.clone(), node.earning_wallet(), *node.rate_pack(), @@ -1276,11 +1268,7 @@ impl Neighborhood { UndesirabilityType::Relay => { node_record.inner.rate_pack.routing_charge(payload_size) as i64 } - UndesirabilityType::ExitRequest(None) => { - node_record.inner.rate_pack.exit_charge(payload_size) as i64 - + node_record.metadata.country_undesirability as i64 - } - UndesirabilityType::ExitRequest(Some(hostname)) => { + UndesirabilityType::ExitRequest(hostname) => { let exit_undesirability = node_record.inner.rate_pack.exit_charge(payload_size) as i64; let country_undesirability = node_record.metadata.country_undesirability as i64; @@ -1326,12 +1314,6 @@ impl Neighborhood { } } - fn advance_return_route_id(&mut self) -> u32 { - let return_route_id = self.next_return_route_id; - self.next_return_route_id = return_route_id.wrapping_add(1); - return_route_id - } - pub fn find_exit_locations<'a>( &'a self, source: &'a PublicKey, @@ -1350,7 +1332,7 @@ impl Neighborhood { PAYLOAD_ZERO_SIZE, RouteDirection::Over, &mut minimum_undesirability, - None, + "booga.com", true, research_exits, ); @@ -1372,7 +1354,7 @@ impl Neighborhood { minimum_hops: usize, payload_size: usize, direction: RouteDirection, - hostname_opt: Option<&str>, + hostname: &str, ) -> Option> { let mut minimum_undesirability = i64::MAX; let initial_undesirability = @@ -1389,7 +1371,7 @@ impl Neighborhood { payload_size, direction, &mut minimum_undesirability, - hostname_opt, + hostname, false, &mut vec![], ) @@ -1413,7 +1395,7 @@ impl Neighborhood { payload_size: usize, direction: RouteDirection, minimum_undesirability: &mut i64, - hostname_opt: Option<&str>, + hostname: &str, research_neighborhood: bool, research_exits: &mut Vec<&'a PublicKey>, ) -> Vec> { @@ -1459,7 +1441,7 @@ impl Neighborhood { payload_size, direction, minimum_undesirability, - hostname_opt, + hostname, research_neighborhood, research_exits, previous_node, @@ -1481,7 +1463,7 @@ impl Neighborhood { payload_size, direction, minimum_undesirability, - hostname_opt, + hostname, research_neighborhood, research_exits, previous_node, @@ -1499,7 +1481,7 @@ impl Neighborhood { payload_size: usize, direction: RouteDirection, minimum_undesirability: &mut i64, - hostname_opt: Option<&str>, + hostname: &str, research_neighborhood: bool, exits_research: &mut Vec<&'a PublicKey>, previous_node: &NodeRecord, @@ -1530,7 +1512,7 @@ impl Neighborhood { new_hops_remaining, payload_size as u64, direction, - hostname_opt, + hostname, ); self.routing_engine( @@ -1541,7 +1523,7 @@ impl Neighborhood { payload_size, direction, minimum_undesirability, - hostname_opt, + hostname, research_neighborhood, exits_research, ) @@ -1598,11 +1580,11 @@ impl Neighborhood { hops_remaining: usize, payload_size: u64, direction: RouteDirection, - hostname_opt: Option<&str>, + hostname: &str, ) -> i64 { let undesirability_type = match (direction, target_opt) { (RouteDirection::Over, None) if hops_remaining == 0 => { - UndesirabilityType::ExitRequest(hostname_opt) + UndesirabilityType::ExitRequest(hostname) } (RouteDirection::Over, _) => UndesirabilityType::Relay, // The exit-and-relay undesirability is initial_undesirability @@ -2164,7 +2146,7 @@ impl UserExitPreferences { #[derive(PartialEq, Eq, Debug)] enum UndesirabilityType<'hostname> { Relay, - ExitRequest(Option<&'hostname str>), + ExitRequest(&'hostname str), ExitAndRouteResponse, } @@ -2185,10 +2167,20 @@ impl<'a> ComputedRouteSegment<'a> { #[cfg(test)] mod tests { + use super::*; use actix::Recipient; use actix::System; use itertools::Itertools; use lazy_static::lazy_static; + use masq_lib::constants::{DEFAULT_CHAIN, TLS_PORT}; + use masq_lib::messages::{ + CountryGroups, ToMessageBody, UiConnectionChangeBroadcast, UiConnectionStage, + }; + use masq_lib::test_utils::utils::{ensure_node_home_directory_exists, TEST_DEFAULT_CHAIN}; + use masq_lib::ui_gateway::MessageBody; + use masq_lib::ui_gateway::MessagePath::Conversation; + use masq_lib::ui_gateway::MessageTarget; + use masq_lib::utils::running_test; use serde_cbor; use std::any::TypeId; use std::cell::RefCell; @@ -2203,29 +2195,28 @@ mod tests { use std::time::Instant; use tokio::prelude::Future; - use masq_lib::constants::{DEFAULT_CHAIN, TLS_PORT}; - use masq_lib::messages::{ - CountryGroups, ToMessageBody, UiConnectionChangeBroadcast, UiConnectionStage, - }; - use masq_lib::test_utils::utils::{ensure_node_home_directory_exists, TEST_DEFAULT_CHAIN}; - use masq_lib::ui_gateway::MessageBody; - use masq_lib::ui_gateway::MessagePath::Conversation; - use masq_lib::ui_gateway::MessageTarget; - use masq_lib::utils::running_test; - + use crate::accountant::test_utils::bc_from_earning_wallet; + use crate::bootstrapper::CryptDEPair; use crate::db_config::persistent_configuration::PersistentConfigError; use crate::neighborhood::gossip::Gossip_0v1; use crate::neighborhood::gossip::{GossipBuilder, GossipNodeRecord}; use crate::neighborhood::node_record::{NodeRecordInner_0v1, NodeRecordInputs}; + use crate::neighborhood::overall_connection_status::ConnectionStageErrors::{ + NoGossipResponseReceived, PassLoopFound, TcpConnectionFailed, + }; + use crate::neighborhood::overall_connection_status::{ + ConnectionProgress, ConnectionStage, OverallConnectionStage, + }; use crate::stream_messages::{NonClandestineAttributes, RemovedStreamType}; use crate::sub_lib::cryptde::{decodex, encodex, CryptData, PlainData}; use crate::sub_lib::cryptde_null::CryptDENull; use crate::sub_lib::dispatcher::Endpoint; use crate::sub_lib::hop::LiveHop; use crate::sub_lib::hopper::MessageType; + use crate::sub_lib::host::Host; + use crate::sub_lib::neighborhood::ExpectedServices::OneWay; use crate::sub_lib::neighborhood::{ - AskAboutDebutGossipMessage, ConfigChange, ConfigChangeMsg, ExpectedServices, - NeighborhoodMode, WalletPair, + AskAboutDebutGossipMessage, ConfigChange, ConfigChangeMsg, NeighborhoodMode, WalletPair, }; use crate::sub_lib::neighborhood::{NeighborhoodConfig, DEFAULT_RATE_PACK}; use crate::sub_lib::neighborhood::{NeighborhoodMetadata, RatePack}; @@ -2248,23 +2239,13 @@ mod tests { 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::notify_handlers::NotifyLaterHandleMock; use crate::test_utils::unshared_test_utils::{ assert_on_initialization_with_panic_on_migration, make_cpm_recipient, make_node_to_ui_recipient, make_recipient_and_recording_arc, prove_that_crash_request_handler_is_hooked_up, AssertionsMessage, }; use crate::test_utils::vec_to_set; - - use super::*; - use crate::accountant::test_utils::bc_from_earning_wallet; - use crate::bootstrapper::CryptDEPair; - use crate::neighborhood::overall_connection_status::ConnectionStageErrors::{ - NoGossipResponseReceived, PassLoopFound, TcpConnectionFailed, - }; - use crate::neighborhood::overall_connection_status::{ - ConnectionProgress, ConnectionStage, OverallConnectionStage, - }; - use crate::test_utils::unshared_test_utils::notify_handlers::NotifyLaterHandleMock; use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; lazy_static! { @@ -3176,7 +3157,7 @@ mod tests { } #[test] - pub fn progress_in_the_stage_of_overall_connection_status_made_by_one_cpm_is_not_overriden_by_the_other( + pub fn progress_in_the_stage_of_overall_connection_status_made_by_one_cpm_is_not_overridden_by_the_other( ) { let peer_1 = make_ip(1); let peer_2 = make_ip(2); @@ -3196,7 +3177,7 @@ mod tests { neighborhood_config, make_wallet("earning"), None, - "progress_in_the_stage_of_overall_connection_status_made_by_one_cpm_is_not_overriden_by_the_other"), + "progress_in_the_stage_of_overall_connection_status_made_by_one_cpm_is_not_overridden_by_the_other"), ); let (node_to_ui_recipient, _) = make_node_to_ui_recipient(); subject.node_to_ui_recipient_opt = Some(node_to_ui_recipient); @@ -3309,7 +3290,10 @@ mod tests { let addr: Addr = subject.start(); let sub: Recipient = addr.recipient::(); - let future = sub.send(RouteQueryMessage::data_indefinite_route_request(None, 400)); + let future = sub.send(RouteQueryMessage::data_indefinite_route_request( + Host::new("booga.com", 1234), + 400, + )); System::current().stop_with_code(0); system.run(); @@ -3325,7 +3309,10 @@ mod tests { let addr: Addr = subject.start(); let sub: Recipient = addr.recipient::(); - let future = sub.send(RouteQueryMessage::data_indefinite_route_request(None, 430)); + let future = sub.send(RouteQueryMessage::data_indefinite_route_request( + Host::new("booga.com", 1234), + 430, + )); System::current().stop_with_code(0); system.run(); @@ -3365,7 +3352,8 @@ mod tests { } let addr: Addr = subject.start(); let sub: Recipient = addr.recipient::(); - let msg = RouteQueryMessage::data_indefinite_route_request(None, 54000); + let msg = + RouteQueryMessage::data_indefinite_route_request(Host::new("booga.com", 1234), 54000); let future = sub.send(msg); @@ -3390,29 +3378,28 @@ mod tests { ), cryptde, None, - 0, None, ) .unwrap(), - expected_services: ExpectedServices::RoundTrip( + expected_services: RoundTrip( vec![ - ExpectedService::Nothing, - ExpectedService::Exit( + Nothing, + Exit( desirable_exit_node.public_key().clone(), desirable_exit_node.earning_wallet(), rate_pack(2345), ), ], vec![ - ExpectedService::Exit( + Exit( desirable_exit_node.public_key().clone(), desirable_exit_node.earning_wallet(), rate_pack(2345), ), - ExpectedService::Nothing, + Nothing, ], - 0, ), + host: Host::new("booga.com", 1234), }; assert_eq!(expected_response, result); } @@ -3425,7 +3412,8 @@ mod tests { subject.min_hops = Hops::TwoHops; let addr: Addr = subject.start(); let sub: Recipient = addr.recipient::(); - let msg = RouteQueryMessage::data_indefinite_route_request(None, 20000); + let msg = + RouteQueryMessage::data_indefinite_route_request(Host::new("booga.com", 1234), 20000); let future = sub.send(msg); @@ -3445,7 +3433,8 @@ mod tests { let sub: Recipient = addr.recipient::(); let future = sub.send(RouteQueryMessage::data_indefinite_route_request( - None, 12345, + Host::new("google.com", 1234), + 12345, )); System::current().stop_with_code(0); @@ -3463,39 +3452,15 @@ mod tests { ), cryptde, None, - 0, None, ) .unwrap(), - expected_services: ExpectedServices::RoundTrip( - vec![ExpectedService::Nothing, ExpectedService::Nothing], - vec![ExpectedService::Nothing, ExpectedService::Nothing], - 0, - ), + expected_services: RoundTrip(vec![Nothing, Nothing], vec![Nothing, Nothing]), + host: Host::new("google.com", 1234), }; assert_eq!(result, expected_response); } - #[test] - fn zero_hop_routing_handles_return_route_id_properly() { - let mut subject = make_standard_subject(); - let result0 = subject.zero_hop_route_response(); - let result1 = subject.zero_hop_route_response(); - - let return_route_id_0 = match result0.expected_services { - ExpectedServices::RoundTrip(_, _, id) => id, - _ => panic!("expected RoundTrip got OneWay"), - }; - - let return_route_id_1 = match result1.expected_services { - ExpectedServices::RoundTrip(_, _, id) => id, - _ => panic!("expected RoundTrip got OneWay"), - }; - - assert_eq!(return_route_id_0, 0); - assert_eq!(return_route_id_1, 1); - } - /* Database: @@ -3541,7 +3506,10 @@ mod tests { let addr: Addr = subject.start(); let sub: Recipient = addr.recipient::(); - let data_route = sub.send(RouteQueryMessage::data_indefinite_route_request(None, 5000)); + let data_route = sub.send(RouteQueryMessage::data_indefinite_route_request( + Host::new("booga.com", 1234), + 5000, + )); System::current().stop_with_code(0); system.run(); @@ -3554,41 +3522,24 @@ mod tests { segment(&[r, q, p], &Component::ProxyServer), cryptde, consuming_wallet_opt, - 0, Some(contract_address), ) .unwrap(), - expected_services: ExpectedServices::RoundTrip( + expected_services: RoundTrip( vec![ - ExpectedService::Nothing, - ExpectedService::Routing( - q.public_key().clone(), - q.earning_wallet(), - rate_pack(3456), - ), - ExpectedService::Exit( - r.public_key().clone(), - r.earning_wallet(), - rate_pack(4567), - ), + Nothing, + Routing(q.public_key().clone(), q.earning_wallet(), rate_pack(3456)), + Exit(r.public_key().clone(), r.earning_wallet(), rate_pack(4567)), ], vec![ - ExpectedService::Exit( - r.public_key().clone(), - r.earning_wallet(), - rate_pack(4567), - ), - ExpectedService::Routing( - q.public_key().clone(), - q.earning_wallet(), - rate_pack(3456), - ), - ExpectedService::Nothing, + Exit(r.public_key().clone(), r.earning_wallet(), rate_pack(4567)), + Routing(q.public_key().clone(), q.earning_wallet(), rate_pack(3456)), + Nothing, ], - 0, ), + host: Host::new("booga.com", 1234), }; - assert_eq!(expected_response, result); + assert_eq!(result, expected_response); } #[test] @@ -3598,6 +3549,7 @@ mod tests { let result: Result = subject.compose_route_query_response( RouteSegment::new(vec![], Component::Neighborhood), RouteSegment::new(vec![], Component::Neighborhood), + Host::new("booga.com", 1234), ); assert!(result.is_err()); let error_expectation: String = result.expect_err("Expected an Err but got:"); @@ -3607,18 +3559,6 @@ mod tests { ); } - #[test] - fn next_return_route_id_wraps_around() { - let mut subject = make_standard_subject(); - subject.next_return_route_id = 0xFFFFFFFF; - - let end = subject.advance_return_route_id(); - let beginning = subject.advance_return_route_id(); - - assert_eq!(end, 0xFFFFFFFF); - assert_eq!(beginning, 0x00000000); - } - /* Database: @@ -3627,37 +3567,6 @@ mod tests { Tests will be written from the viewpoint of O. */ - #[test] - fn return_route_ids_increase() { - let cryptde = CRYPTDE_PAIR.main.as_ref(); - let system = System::new("return_route_ids_increase"); - let (_, _, _, mut subject) = make_o_r_e_subject(); - subject.min_hops = Hops::TwoHops; - let addr: Addr = subject.start(); - let sub: Recipient = addr.recipient::(); - - let data_route_0 = sub.send(RouteQueryMessage::data_indefinite_route_request(None, 2000)); - let data_route_1 = sub.send(RouteQueryMessage::data_indefinite_route_request(None, 3000)); - - System::current().stop_with_code(0); - system.run(); - let result_0 = data_route_0.wait().unwrap().unwrap(); - let result_1 = data_route_1.wait().unwrap().unwrap(); - let juicy_parts = |result: RouteQueryResponse| { - let last_element = result.route.hops.last().unwrap(); - let last_element_dec = cryptde.decode(last_element).unwrap(); - let network_return_route_id: u32 = - serde_cbor::de::from_slice(last_element_dec.as_slice()).unwrap(); - let metadata_return_route_id = match result.expected_services { - ExpectedServices::RoundTrip(_, _, id) => id, - _ => panic!("expected RoundTrip got OneWay"), - }; - (network_return_route_id, metadata_return_route_id) - }; - assert_eq!(juicy_parts(result_0), (0, 0)); - assert_eq!(juicy_parts(result_1), (1, 1)); - } - #[test] fn handle_neighborhood_graph_message_works() { let test_name = "handle_neighborhood_graph_message_works"; @@ -4518,6 +4427,7 @@ mod tests { let result: Result = subject.compose_route_query_response( RouteSegment::new(vec![], Component::ProxyClient), RouteSegment::new(vec![], Component::ProxyServer), + Host::new("booga.com", 1234), ); assert!(result.is_err()); let error_expectation: String = result.expect_err("Expected an Err but got:"); @@ -4534,6 +4444,7 @@ mod tests { let result: Result = subject.compose_route_query_response( RouteSegment::new(vec![&PublicKey::new(&[3, 3, 8])], Component::ProxyClient), RouteSegment::new(vec![&PublicKey::new(&[8, 3, 3])], Component::ProxyServer), + Host::new("booga.com", 1234), ); assert!(result.is_err()); let error_expectation: String = result.expect_err("Expected an Err but got:"); @@ -4541,7 +4452,6 @@ mod tests { error_expectation, "Cannot make multi_hop with unknown neighbor" ); - assert_eq!(subject.next_return_route_id, 0); } #[test] @@ -4663,34 +4573,58 @@ mod tests { // At least two hops from p to anywhere standard let route_opt = - subject.find_best_route_segment(p, None, 2, 10000, RouteDirection::Over, None); + subject.find_best_route_segment(p, None, 2, 10000, RouteDirection::Over, "booga.com"); assert_eq!(route_opt.unwrap(), vec![p, s, t]); // no [p, r, s] or [p, s, r] because s and r are both neighbors of p and can't exit for it // At least two hops over from p to t - let route_opt = - subject.find_best_route_segment(p, Some(t), 2, 10000, RouteDirection::Over, None); + let route_opt = subject.find_best_route_segment( + p, + Some(t), + 2, + 10000, + RouteDirection::Over, + "booga.com", + ); assert_eq!(route_opt.unwrap(), vec![p, s, t]); // At least two hops over from t to p - let route_opt = - subject.find_best_route_segment(t, Some(p), 2, 10000, RouteDirection::Over, None); + let route_opt = subject.find_best_route_segment( + t, + Some(p), + 2, + 10000, + RouteDirection::Over, + "booga.com", + ); assert_eq!(route_opt, None); // p is consume-only; can't be an exit Node. // At least two hops back from t to p - let route_opt = - subject.find_best_route_segment(t, Some(p), 2, 10000, RouteDirection::Back, None); + let route_opt = subject.find_best_route_segment( + t, + Some(p), + 2, + 10000, + RouteDirection::Back, + "booga.com", + ); assert_eq!(route_opt.unwrap(), vec![t, s, p]); // p is consume-only, but it's the originating Node, so including it is okay // At least two hops from p to Q - impossible - let route_opt = - subject.find_best_route_segment(p, Some(q), 2, 10000, RouteDirection::Over, None); + let route_opt = subject.find_best_route_segment( + p, + Some(q), + 2, + 10000, + RouteDirection::Over, + "booga.com", + ); assert_eq!(route_opt, None); } @@ -4735,7 +4669,7 @@ mod tests { 3, 10000, RouteDirection::Back, - None, + "booga.com", ) .unwrap(); @@ -4810,7 +4744,7 @@ mod tests { 3, 10000, RouteDirection::Over, - None, + "booga.com", ); let after = Instant::now(); @@ -4861,8 +4795,14 @@ mod tests { db.add_arbitrary_full_neighbor(c_au_key, a_fr_key); subject.handle_exit_location_message(message, 0, 0); - let route_cz = - subject.find_best_route_segment(root_key, None, 2, 10000, RouteDirection::Over, None); + let route_cz = subject.find_best_route_segment( + root_key, + None, + 2, + 10000, + RouteDirection::Over, + "booga.com", + ); assert_eq!(route_cz, None); } @@ -4927,7 +4867,7 @@ mod tests { subject_min_hops, 10000, RouteDirection::Over, - None, + "booga.com", ); let exit_node = cdb.node_by_key(&route_au.as_ref().unwrap().last().unwrap()); @@ -4982,8 +4922,14 @@ mod tests { }; subject.handle_exit_location_message(message, 0, 0); - let route_fr = - subject.find_best_route_segment(root_key, None, 2, 10000, RouteDirection::Over, None); + let route_fr = subject.find_best_route_segment( + root_key, + None, + 2, + 10000, + RouteDirection::Over, + "booga.com", + ); let exit_node = cdb.node_by_key(&route_fr.as_ref().unwrap().last().unwrap()); assert_eq!( @@ -5006,7 +4952,7 @@ mod tests { // At least two hops from P to anywhere standard let route_opt = - subject.find_best_route_segment(p, None, 2, 10000, RouteDirection::Over, None); + subject.find_best_route_segment(p, None, 2, 10000, RouteDirection::Over, "booga.com"); assert_eq!(route_opt, None); } @@ -5023,7 +4969,7 @@ mod tests { 5, // Lots of hops to go yet 1_000, RouteDirection::Over, - Some("hostname.com"), + "hostname.com", ); let rate_pack = node_record.rate_pack(); @@ -5047,7 +4993,7 @@ mod tests { 0, // Last hop 1_000, RouteDirection::Over, - Some("hostname.com"), + "hostname.com", ); let rate_pack = node_record.rate_pack(); @@ -5075,7 +5021,7 @@ mod tests { 0, // Last hop 1_000, RouteDirection::Over, - Some("hostname.com"), + "hostname.com", ); let rate_pack = node_record.rate_pack(); @@ -5148,7 +5094,7 @@ mod tests { 5, // Plenty of hops remaining: not there yet 1_000, RouteDirection::Back, - None, + "booga.com", ); let rate_pack = node_record.rate_pack(); @@ -6622,7 +6568,7 @@ mod tests { target_component: Component::ProxyClient, return_component_opt: None, payload_size: 10000, - hostname_opt: None, + host: Host::new("booga.com", 1234), }; let unsuccessful_three_hop_route = addr.send(three_hop_route_request); let asserted_node_record = a.clone(); @@ -6672,7 +6618,7 @@ mod tests { context: TransmitDataMsg { endpoint: Endpoint::Key(cryptde.public_key().clone()), last_data: false, - sequence_number: None, + sequence_number_opt: None, data: Vec::new(), }, recipient, @@ -6735,7 +6681,7 @@ mod tests { context: TransmitDataMsg { endpoint: Endpoint::Key(cryptde.public_key().clone()), last_data: false, - sequence_number: None, + sequence_number_opt: None, data: Vec::new(), }, recipient, @@ -6764,7 +6710,7 @@ mod tests { let context = TransmitDataMsg { endpoint: Endpoint::Key(cryptde.public_key().clone()), last_data: false, - sequence_number: None, + sequence_number_opt: None, data: Vec::new(), }; let context_a = context.clone(); @@ -6864,7 +6810,7 @@ mod tests { context: TransmitDataMsg { endpoint: Endpoint::Key(cryptde.public_key().clone()), last_data: false, - sequence_number: None, + sequence_number_opt: None, data: Vec::new(), }, recipient, @@ -6892,7 +6838,7 @@ mod tests { let context = TransmitDataMsg { endpoint: Endpoint::Key(cryptde.public_key().clone()), last_data: false, - sequence_number: None, + sequence_number_opt: None, data: Vec::new(), }; let context_a = context.clone(); @@ -6989,7 +6935,7 @@ mod tests { target_component: Component::ProxyClient, return_component_opt: Some(Component::ProxyServer), payload_size: 10000, - hostname_opt: None, + host: Host::new("booga.com", 1234), }); assert_eq!( @@ -7035,16 +6981,16 @@ mod tests { target_component: Component::ProxyClient, return_component_opt: Some(Component::ProxyServer), payload_size: 10000, - hostname_opt: None, + host: Host::new("host.name", 88), }); let next_door_neighbor_cryptde = CryptDENull::from(&next_door_neighbor.public_key(), TEST_DEFAULT_CHAIN); let exit_node_cryptde = CryptDENull::from(&exit_node.public_key(), TEST_DEFAULT_CHAIN); - - let hops = result.clone().unwrap().route.hops; + let response = result.clone().unwrap(); + let hops = &response.route.hops; let actual_keys: Vec = match hops.as_slice() { - [hop, exit, hop_back, origin, empty, _accounting] => vec![ + [hop, exit, hop_back, origin, empty] => vec![ decodex::(CRYPTDE_PAIR.main.as_ref(), hop) .expect("hop") .public_key, @@ -7061,7 +7007,11 @@ mod tests { .expect("empty") .public_key, ], - l => panic!("our match is wrong, real size is {}, {:?}", l.len(), l), + l => panic!( + "our match is wrong, real size is {} instead of 5, {:?}", + l.len(), + l + ), }; let expected_public_keys = vec![ next_door_neighbor.public_key().clone(), @@ -7071,6 +7021,38 @@ mod tests { PublicKey::new(b""), ]; assert_eq!(expected_public_keys, actual_keys); + assert_eq!( + response.expected_services, + RoundTrip( + vec![ + Nothing, + Routing( + next_door_neighbor.public_key().clone(), + next_door_neighbor.earning_wallet(), + next_door_neighbor.rate_pack().clone() + ), + Exit( + exit_node.public_key().clone(), + exit_node.earning_wallet(), + exit_node.rate_pack().clone() + ), + ], + vec![ + Exit( + exit_node.public_key().clone(), + exit_node.earning_wallet(), + exit_node.rate_pack().clone() + ), + Routing( + next_door_neighbor.public_key().clone(), + next_door_neighbor.earning_wallet(), + next_door_neighbor.rate_pack().clone() + ), + Nothing, + ] + ) + ); + assert_eq!(response.host, Host::new("host.name", 88)); } fn assert_route_query_message(min_hops: Hops) { @@ -7089,7 +7071,7 @@ mod tests { target_component: Component::ProxyClient, return_component_opt: Some(Component::ProxyServer), payload_size: 10000, - hostname_opt: None, + host: Host::new("booga.com", 1234), }); let assert_hops = |cryptdes: Vec, route: &[CryptData]| { @@ -7099,24 +7081,23 @@ mod tests { } }; /* - This is how the route_hops vector looks like: [C1, C2, ..., C(nodes_count), ..., C2, C1, accounting] + This is how the route_hops vector looks like: [C1, C2, ..., C(nodes_count), ..., C2, C1] Let's consider for 3-hop route ==> Nodes Count --> 4 Route Length --> 8 - Route Hops --> [C1, C2, C3, C4, C3, C2, C1, accounting] + Route Hops --> [C1, C2, C3, C4, C3, C2, C1] Over Route --> [C1, C2, C3] Back Route --> [C4, C3, C2, C1] */ - let mut route_hops = result.unwrap().route.hops; + let route_hops = result.unwrap().route.hops; let route_length = route_hops.len(); - let _accounting = route_hops.pop(); let over_route = &route_hops[..hops]; let back_route = &route_hops[hops..]; let over_cryptdes = cryptdes_from_node_records(&nodes[..hops]); let mut back_cryptdes = cryptdes_from_node_records(&nodes); back_cryptdes.reverse(); - assert_eq!(route_length, 2 * nodes_count); + assert_eq!(route_length, 2 * nodes_count - 1); assert_hops(over_cryptdes, over_route); assert_hops(back_cryptdes, back_route); } @@ -7190,16 +7171,16 @@ mod tests { target_component: Component::ProxyClient, return_component_opt: Some(Component::ProxyServer), payload_size, - hostname_opt: None, + host: Host::new("booga.com", 1234), }) .unwrap(); let (over, back) = match response.expected_services { - ExpectedServices::OneWay(_) => panic!("Expecting RoundTrip"), - ExpectedServices::RoundTrip(o, b, _) => (o[1].clone(), b[1].clone()), + OneWay(_) => panic!("Expecting RoundTrip"), + RoundTrip(o, b) => (o[1].clone(), b[1].clone()), }; let extract_key = |es: ExpectedService| match es { - ExpectedService::Routing(pk, _, _) => pk, + Routing(pk, _, _) => pk, x => panic!("Expecting Routing, found {:?}", x), }; let expected_relay_key = if a_not_b { a.clone() } else { b.clone() }; @@ -7591,24 +7572,6 @@ mod tests { subject } - fn make_o_r_e_subject() -> (NodeRecord, NodeRecord, NodeRecord, Neighborhood) { - let mut subject = make_standard_subject(); - let o = &subject.neighborhood_database.root().clone(); - let r = &make_node_record(4567, false); - let e = &make_node_record(5678, false); - { - let db = &mut subject.neighborhood_database; - db.add_node(r.clone()).unwrap(); - db.add_node(e.clone()).unwrap(); - let mut dual_edge = |a: &NodeRecord, b: &NodeRecord| { - db.add_arbitrary_full_neighbor(a.public_key(), b.public_key()) - }; - dual_edge(o, r); - dual_edge(r, e); - } - (o.clone(), r.clone(), e.clone(), subject) - } - fn segment(nodes: &[&NodeRecord], component: &Component) -> RouteSegment { RouteSegment::new( nodes.into_iter().map(|n| n.public_key()).collect(), diff --git a/node/src/proxy_client/mod.rs b/node/src/proxy_client/mod.rs index aa0f33010..c17daaf16 100644 --- a/node/src/proxy_client/mod.rs +++ b/node/src/proxy_client/mod.rs @@ -613,7 +613,7 @@ mod tests { sequence_number: 0, last_data: false, }, - target_hostname: Some(String::from("target.hostname.com")), + target_hostname: String::from("target.hostname.com"), target_port: 1234, protocol: ProxyProtocol::HTTP, originator_public_key: PublicKey::new(&b"originator_public_key"[..]), @@ -622,7 +622,7 @@ mod tests { let package = ExpiredCoresPackage::new( SocketAddr::from_str("1.2.3.4:1234").unwrap(), Some(make_wallet("consuming")), - route_to_proxy_client(&cryptde.public_key(), cryptde), + route_to_proxy_client(&cryptde.public_key(), cryptde, false), request, 0, ); @@ -754,7 +754,7 @@ mod tests { sequence_number: 0, last_data: false, }, - target_hostname: None, + target_hostname: "booga.com".to_string(), target_port: 0, protocol: ProxyProtocol::HTTP, originator_public_key: PublicKey::new(&b"originator"[..]), @@ -813,7 +813,7 @@ mod tests { sequence_number: 0, last_data: false, }, - target_hostname: None, + target_hostname: "booga.com".to_string(), target_port: 0, protocol: ProxyProtocol::HTTP, originator_public_key: PublicKey::new(&b"originator"[..]), @@ -870,7 +870,7 @@ mod tests { sequence_number: 0, last_data: false, }, - target_hostname: None, + target_hostname: "booga.com".to_string(), target_port: 0, protocol: ProxyProtocol::HTTP, originator_public_key: alias_cryptde.public_key().clone(), @@ -1226,7 +1226,7 @@ mod tests { sequence_number: 0, last_data: false, }, - target_hostname: None, + target_hostname: "booga.com".to_string(), target_port: 0, protocol: ProxyProtocol::HTTP, originator_public_key: originator_public_key.clone(), diff --git a/node/src/proxy_client/stream_establisher.rs b/node/src/proxy_client/stream_establisher.rs index 602b3c0c1..a07e4d730 100644 --- a/node/src/proxy_client/stream_establisher.rs +++ b/node/src/proxy_client/stream_establisher.rs @@ -160,6 +160,8 @@ mod tests { fn spawn_stream_reader_handles_data() { let (proxy_client, proxy_client_awaiter, proxy_client_recording_arc) = make_recorder(); let (sub_tx, sub_rx) = unbounded(); + let stream_key = StreamKey::make_meaningless_stream_key(); + let stream_key_inner = stream_key.clone(); thread::spawn(move || { let system = System::new("spawn_stream_reader_handles_data"); let peer_actors = peer_actors_builder().proxy_client(proxy_client).build(); @@ -193,13 +195,13 @@ mod tests { }; subject.spawn_stream_reader( &ClientRequestPayload_0v1 { - stream_key: StreamKey::make_meaningless_stream_key(), + stream_key: stream_key_inner, sequenced_packet: SequencedPacket { data: vec![], sequence_number: 0, last_data: false, }, - target_hostname: Some("blah".to_string()), + target_hostname: "blah".to_string(), target_port: 0, protocol: ProxyProtocol::HTTP, originator_public_key: subject.cryptde.public_key().clone(), @@ -227,7 +229,7 @@ mod tests { assert_eq!( ibsd, InboundServerData { - stream_key: StreamKey::make_meaningless_stream_key(), + stream_key, last_data: false, sequence_number: 0, source: SocketAddr::from_str("1.2.3.4:5678").unwrap(), diff --git a/node/src/proxy_client/stream_handler_pool.rs b/node/src/proxy_client/stream_handler_pool.rs index 7fc623617..8a42ca2ae 100644 --- a/node/src/proxy_client/stream_handler_pool.rs +++ b/node/src/proxy_client/stream_handler_pool.rs @@ -114,11 +114,7 @@ impl StreamHandlerPoolReal { let inner_arc_1 = inner_arc.clone(); let logger = Self::make_logger_copy(&inner_arc); let data_len = payload.sequenced_packet.data.len(); - let hostname = payload - .target_hostname - .as_ref() - .unwrap_or(&"".to_string()) - .to_string(); + let hostname = payload.target_hostname.clone(); let target_port = payload.target_port; match Self::find_stream_with_key(&stream_key, &inner_arc) { Some(sender_wrapper) => { @@ -315,27 +311,14 @@ impl StreamHandlerPoolReal { "No stream to {:?} exists; resolving host", &payload.target_hostname ); - match payload.target_hostname { - Some(ref target_hostname) => match Self::parse_ip(target_hostname) { - Ok(socket_addr) => Self::handle_ip( - payload.clone(), - socket_addr, - inner_arc, - target_hostname.to_string(), - ), - Err(_) => Self::lookup_dns(inner_arc, target_hostname.to_string(), payload.clone()), - }, - None => { - error!( - logger, - "Cannot open new stream with key {:?}: no hostname supplied", - payload.stream_key - ); - Box::new(err::< - Box + 'static>, - String, - >("No hostname provided".to_string())) - } + match Self::parse_ip(&payload.target_hostname) { + Ok(socket_addr) => Self::handle_ip( + payload.clone(), + socket_addr, + inner_arc, + payload.target_hostname.clone(), + ), + Err(_) => Self::lookup_dns(inner_arc, payload.target_hostname.clone(), payload.clone()), } } @@ -732,7 +715,7 @@ mod tests { let payload = ClientRequestPayload_0v1 { stream_key, sequenced_packet: SequencedPacket::new(b"booga".to_vec(), 0, false), - target_hostname: Some("www.example.com".to_string()), + target_hostname: "www.example.com".to_string(), target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: cryptde.public_key().clone(), @@ -765,7 +748,7 @@ mod tests { sequence_number: 0, last_data: false, }, - target_hostname: None, + target_hostname: "booga.com".to_string(), target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: PublicKey::new(&b"men's souls"[..]), @@ -820,18 +803,20 @@ mod tests { init_test_logging(); let test_name = "write_failure_for_nonexistent_stream_generates_termination_message"; let cryptde = CRYPTDE_PAIR.main.as_ref(); + let stream_key = StreamKey::make_meaningless_stream_key(); + let stream_key_inner = stream_key.clone(); let (proxy_client, proxy_client_awaiter, proxy_client_recording_arc) = make_recorder(); let originator_key = PublicKey::new(&b"men's souls"[..]); let (reader_shutdown_tx, reader_shutdown_rx) = unbounded(); thread::spawn(move || { let client_request_payload = ClientRequestPayload_0v1 { - stream_key: StreamKey::make_meaningless_stream_key(), + stream_key: stream_key_inner, sequenced_packet: SequencedPacket { data: b"These are the times".to_vec(), sequence_number: 0, last_data: false, }, - target_hostname: Some(String::from("that.try")), + target_hostname: String::from("that.try"), target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: originator_key, @@ -879,7 +864,7 @@ mod tests { assert_eq!( proxy_client_recording.get_record::(0), &InboundServerData { - stream_key: StreamKey::make_meaningless_stream_key(), + stream_key: stream_key.clone(), last_data: true, sequence_number: 0, source: SocketAddr::from_str("2.3.4.5:80").unwrap(), @@ -888,7 +873,8 @@ mod tests { ); TestLogHandler::new().exists_log_containing(&format!( "DEBUG: {test_name}: A shutdown signal was sent to the StreamReader \ - for stream key AAAAAAAAAAAAAAAAAAAAAAAAAAA." + for stream key {}.", + stream_key )); } @@ -897,17 +883,19 @@ mod tests { let cryptde = CRYPTDE_PAIR.main.as_ref(); let write_parameters = Arc::new(Mutex::new(vec![])); let expected_write_parameters = write_parameters.clone(); + let stream_key = StreamKey::make_meaningless_stream_key(); + let stream_key_inner = stream_key.clone(); let (proxy_client, proxy_client_awaiter, proxy_client_recording_arc) = make_recorder(); thread::spawn(move || { let peer_actors = peer_actors_builder().proxy_client(proxy_client).build(); let client_request_payload = ClientRequestPayload_0v1 { - stream_key: StreamKey::make_meaningless_stream_key(), + stream_key: stream_key_inner, sequenced_packet: SequencedPacket { data: b"These are the times".to_vec(), sequence_number: 0, last_data: false, }, - target_hostname: Some(String::from("3.4.5.6:80")), + target_hostname: String::from("3.4.5.6:80"), target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: PublicKey::new(&b"men's souls"[..]), @@ -980,7 +968,7 @@ mod tests { assert_eq!( proxy_client_recording.get_record::(0), &InboundServerData { - stream_key: StreamKey::make_meaningless_stream_key(), + stream_key, last_data: false, sequence_number: 0, source: SocketAddr::from_str("3.4.5.6:80").unwrap(), @@ -1003,7 +991,7 @@ mod tests { sequence_number: 0, last_data: true, }, - target_hostname: Some(String::from("3.4.5.6:80")), + target_hostname: String::from("3.4.5.6:80"), target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: PublicKey::new(&b"brutal death"[..]), @@ -1064,7 +1052,7 @@ mod tests { sequence_number: 0, last_data: true, }, - target_hostname: Some(String::from("3.4.5.6:80")), + target_hostname: String::from("3.4.5.6:80"), target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: PublicKey::new(&b"brutal death"[..]), @@ -1116,17 +1104,19 @@ mod tests { let expected_lookup_ip_parameters = lookup_ip_parameters.clone(); let write_parameters = Arc::new(Mutex::new(vec![])); let expected_write_parameters = write_parameters.clone(); + let stream_key = StreamKey::make_meaningless_stream_key(); + let stream_key_inner = stream_key.clone(); let (proxy_client, proxy_client_awaiter, proxy_client_recording_arc) = make_recorder(); thread::spawn(move || { let peer_actors = peer_actors_builder().proxy_client(proxy_client).build(); let client_request_payload = ClientRequestPayload_0v1 { - stream_key: StreamKey::make_meaningless_stream_key(), + stream_key: stream_key_inner, sequenced_packet: SequencedPacket { data: b"These are the times".to_vec(), sequence_number: 0, last_data: false, }, - target_hostname: Some(String::from("3.4.5.6")), + target_hostname: String::from("3.4.5.6"), target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: PublicKey::new(&b"men's souls"[..]), @@ -1209,7 +1199,7 @@ mod tests { assert_eq!( proxy_client_recording.get_record::(0), &InboundServerData { - stream_key: StreamKey::make_meaningless_stream_key(), + stream_key, last_data: false, sequence_number: 0, source: SocketAddr::from_str("3.4.5.6:80").unwrap(), @@ -1218,71 +1208,6 @@ mod tests { ); } - #[test] - fn missing_hostname_for_nonexistent_stream_generates_log_and_termination_message() { - init_test_logging(); - let test_name = - "missing_hostname_for_nonexistent_stream_generates_log_and_termination_message"; - let cryptde = CRYPTDE_PAIR.main.as_ref(); - let (proxy_client, proxy_client_awaiter, proxy_client_recording_arc) = make_recorder(); - let originator_key = PublicKey::new(&b"men's souls"[..]); - let stream_key = StreamKey::make_meaningful_stream_key(test_name); - thread::spawn(move || { - let peer_actors = peer_actors_builder().proxy_client(proxy_client).build(); - let client_request_payload = ClientRequestPayload_0v1 { - stream_key: stream_key.clone(), - sequenced_packet: SequencedPacket { - data: b"These are the times".to_vec(), - sequence_number: 0, - last_data: false, - }, - target_hostname: None, - target_port: HTTP_PORT, - protocol: ProxyProtocol::HTTP, - originator_public_key: originator_key, - }; - let package = ExpiredCoresPackage::new( - SocketAddr::from_str("1.2.3.4:1234").unwrap(), - Some(make_wallet("consuming")), - make_meaningless_route(&CRYPTDE_PAIR), - client_request_payload.into(), - 0, - ); - let resolver = - ResolverWrapperMock::new().lookup_ip_failure(ResolveErrorKind::Io.into()); - let subject = StreamHandlerPoolReal::new( - Box::new(resolver), - cryptde, - peer_actors.accountant.report_exit_service_provided.clone(), - peer_actors.proxy_client_opt.unwrap().clone(), - 100, - 200, - ); - - run_process_package_in_actix(subject, package); - }); - - proxy_client_awaiter.await_message_count(1); - let proxy_client_recording = proxy_client_recording_arc.lock().unwrap(); - assert_eq!( - proxy_client_recording.get_record::(0), - &InboundServerData { - stream_key: stream_key.clone(), - last_data: true, - sequence_number: 0, - source: error_socket_addr(), - data: vec![], - } - ); - TestLogHandler::new().exists_log_containing( - format!( - "ERROR: ProxyClient: Cannot open new stream with key {:?}: no hostname supplied", - stream_key - ) - .as_str(), - ); - } - #[test] fn nonexistent_connection_springs_into_being_and_is_persisted_to_handle_transaction() { let cryptde = CRYPTDE_PAIR.main.as_ref(); @@ -1290,6 +1215,8 @@ mod tests { let expected_lookup_ip_parameters = lookup_ip_parameters.clone(); let write_parameters = Arc::new(Mutex::new(vec![])); let expected_write_parameters = write_parameters.clone(); + let stream_key = StreamKey::make_meaningless_stream_key(); + let stream_key_inner = stream_key.clone(); let (proxy_client, proxy_client_awaiter, proxy_client_recording_arc) = make_recorder(); let (accountant, accountant_awaiter, accountant_recording_arc) = make_recorder(); let before = SystemTime::now(); @@ -1299,13 +1226,13 @@ mod tests { .accountant(accountant) .build(); let client_request_payload = ClientRequestPayload_0v1 { - stream_key: StreamKey::make_meaningless_stream_key(), + stream_key: stream_key_inner, sequenced_packet: SequencedPacket { data: b"These are the times".to_vec(), sequence_number: 0, last_data: false, }, - target_hostname: Some(String::from("that.try")), + target_hostname: String::from("that.try"), target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: PublicKey::new(&b"men's souls"[..]), @@ -1390,7 +1317,7 @@ mod tests { assert_eq!( proxy_client_recording.get_record::(0), &InboundServerData { - stream_key: StreamKey::make_meaningless_stream_key(), + stream_key, last_data: false, sequence_number: 0, source: SocketAddr::from_str("3.4.5.6:80").unwrap(), @@ -1418,7 +1345,7 @@ mod tests { sequence_number: 0, last_data: false, }, - target_hostname: Some(String::from("that.try")), + target_hostname: String::from("that.try"), target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: originator_key, @@ -1540,7 +1467,7 @@ mod tests { sequence_number: 0, last_data: false, }, - target_hostname: Some(String::from("blockedwebsite.com")), + target_hostname: String::from("blockedwebsite.com"), target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: originator_key, @@ -1637,7 +1564,7 @@ mod tests { let client_request_payload = ClientRequestPayload_0v1 { stream_key, sequenced_packet: sequenced_packet.clone(), - target_hostname: Some(String::from("that.try")), + target_hostname: String::from("that.try"), target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: PublicKey::new(&b"men's souls"[..]), @@ -1750,7 +1677,7 @@ mod tests { sequence_number: 0, last_data: true, }, - target_hostname: Some(String::from("that.try")), + target_hostname: String::from("that.try"), target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: originator_key, @@ -1812,7 +1739,7 @@ mod tests { let client_request_payload = ClientRequestPayload_0v1 { stream_key: stream_key.clone(), sequenced_packet: sequenced_packet.clone(), - target_hostname: Some(String::from("that.try")), + target_hostname: String::from("that.try"), target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: PublicKey::new(&b"men's souls"[..]), @@ -1883,7 +1810,7 @@ mod tests { sequence_number: 0, last_data: false, }, - target_hostname: None, + target_hostname: "booga.com".to_string(), target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: PublicKey::new(&b"booga"[..]), diff --git a/node/src/proxy_client/stream_reader.rs b/node/src/proxy_client/stream_reader.rs index 992b58dbf..bf39de6e3 100644 --- a/node/src/proxy_client/stream_reader.rs +++ b/node/src/proxy_client/stream_reader.rs @@ -179,9 +179,10 @@ mod tests { }); let proxy_client_sub = rx.recv().unwrap(); + let stream_key = StreamKey::make_meaningless_stream_key(); let (stream_killer, stream_killer_params) = unbounded(); let mut subject = StreamReader { - stream_key: StreamKey::make_meaningless_stream_key(), + stream_key: stream_key.clone(), proxy_client_sub, stream, stream_killer, @@ -198,7 +199,7 @@ mod tests { assert_eq!( proxy_client_recording.get_record::(0), &InboundServerData { - stream_key: StreamKey::make_meaningless_stream_key(), + stream_key: stream_key.clone(), last_data: false, sequence_number: 0, source: SocketAddr::from_str("8.7.4.3:50").unwrap(), @@ -208,7 +209,7 @@ mod tests { assert_eq!( proxy_client_recording.get_record::(1), &InboundServerData { - stream_key: StreamKey::make_meaningless_stream_key(), + stream_key: stream_key.clone(), last_data: false, sequence_number: 1, source: SocketAddr::from_str("8.7.4.3:50").unwrap(), @@ -218,7 +219,7 @@ mod tests { assert_eq!( proxy_client_recording.get_record::(2), &InboundServerData { - stream_key: StreamKey::make_meaningless_stream_key(), + stream_key: stream_key.clone(), last_data: false, sequence_number: 2, source: SocketAddr::from_str("8.7.4.3:50").unwrap(), @@ -226,10 +227,7 @@ mod tests { }, ); let stream_killer_parameters = stream_killer_params.try_recv().unwrap(); - assert_eq!( - stream_killer_parameters, - (StreamKey::make_meaningless_stream_key(), 3) - ); + assert_eq!(stream_killer_parameters, (stream_key, 3)); } #[test] @@ -266,6 +264,7 @@ mod tests { let (stream_killer, stream_killer_params) = unbounded(); let peer_addr = SocketAddr::from_str("5.7.9.0:95").unwrap(); let mut subject = make_subject(); + let stream_key = subject.stream_key.clone(); subject.proxy_client_sub = proxy_client_sub; subject.stream = Box::new(stream); subject.stream_killer = stream_killer; @@ -279,7 +278,7 @@ mod tests { assert_eq!( proxy_client_recording.get_record::(0), &InboundServerData { - stream_key: StreamKey::make_meaningless_stream_key(), + stream_key: stream_key.clone(), last_data: false, sequence_number: 0, source: peer_addr, @@ -289,7 +288,7 @@ mod tests { assert_eq!( proxy_client_recording.get_record::(1), &InboundServerData { - stream_key: StreamKey::make_meaningless_stream_key(), + stream_key: stream_key.clone(), last_data: false, sequence_number: 1, source: peer_addr, @@ -299,7 +298,7 @@ mod tests { assert_eq!( proxy_client_recording.get_record::(2), &InboundServerData { - stream_key: StreamKey::make_meaningless_stream_key(), + stream_key: stream_key.clone(), last_data: false, sequence_number: 2, source: peer_addr, @@ -310,10 +309,7 @@ mod tests { let kill_stream_msg = stream_killer_params .try_recv() .expect("stream was not killed"); - assert_eq!( - kill_stream_msg, - (StreamKey::make_meaningless_stream_key(), 3) - ); + assert_eq!(kill_stream_msg, (stream_key, 3)); assert!(stream_killer_params.try_recv().is_err()); } diff --git a/node/src/proxy_server/client_request_payload_factory.rs b/node/src/proxy_server/client_request_payload_factory.rs index 75a484a37..4f9e5df60 100644 --- a/node/src/proxy_server/client_request_payload_factory.rs +++ b/node/src/proxy_server/client_request_payload_factory.rs @@ -3,6 +3,7 @@ use crate::proxy_server::protocol_pack::from_ibcd; use crate::sub_lib::cryptde::CryptDE; use crate::sub_lib::cryptde::PlainData; use crate::sub_lib::dispatcher::InboundClientData; +use crate::sub_lib::host::Host; use crate::sub_lib::proxy_server::ClientRequestPayload_0v1; use crate::sub_lib::sequence_buffer::SequencedPacket; use crate::sub_lib::stream_key::StreamKey; @@ -13,6 +14,7 @@ pub trait ClientRequestPayloadFactory { &self, ibcd: &InboundClientData, stream_key: StreamKey, + host_opt: Option, cryptde: &dyn CryptDE, logger: &Logger, ) -> Option; @@ -26,11 +28,44 @@ impl ClientRequestPayloadFactory for ClientRequestPayloadFactoryReal { &self, ibcd: &InboundClientData, stream_key: StreamKey, + host_from_history_opt: Option, cryptde: &dyn CryptDE, logger: &Logger, ) -> Option { let protocol_pack = from_ibcd(ibcd).map_err(|e| error!(logger, "{}", e)).ok()?; - let sequence_number = match ibcd.sequence_number { + let host_from_request_result_closure = Box::new(|| { + let data = PlainData::new(&ibcd.data); + match protocol_pack.find_host(&data) { + Some(host) => Ok(host), + // So far we've only looked in the client packet; but this message will evaporate + // unless there's no host information in host_opt (from ProxyServer's StreamInfo) either. + None => Err(format!( + "No hostname information found in either client packet ({}) or ProxyServer for protocol {:?}, with StreamKey {}", + protocol_pack.describe_packet(&data), + protocol_pack.proxy_protocol(), + stream_key + )), + } + }); + let target_host = match (host_from_request_result_closure(), host_from_history_opt) { + (Ok(host), _) => host, + (Err(_), Some(host)) => host, + (Err(e), None) => { + if ibcd.last_data && ibcd.data.is_empty() { + warning!( + logger, + "Client opened {:?} connection and immediately closed it without sending any data, with StreamKey {}", + protocol_pack.proxy_protocol(), + stream_key + ); + } else { + error!(logger, "{}", e); + } + error!(logger, "{}", e); + return None; + } + }; + let sequence_number = match ibcd.sequence_number_opt { Some(sequence_number) => sequence_number, None => { error!( @@ -41,12 +76,6 @@ impl ClientRequestPayloadFactory for ClientRequestPayloadFactoryReal { return None; } }; - let data = PlainData::new(&ibcd.data); - let target_host = protocol_pack.find_host(&data); - let (target_hostname_opt, target_port) = match target_host { - Some(host) => (Some(host.name), host.port), - None => (None, protocol_pack.standard_port()), - }; Some(ClientRequestPayload_0v1 { stream_key, sequenced_packet: SequencedPacket { @@ -54,8 +83,8 @@ impl ClientRequestPayloadFactory for ClientRequestPayloadFactoryReal { sequence_number, last_data: ibcd.last_data, }, - target_hostname: target_hostname_opt, - target_port, + target_hostname: target_host.name, + target_port: target_host.port, protocol: protocol_pack.proxy_protocol(), originator_public_key: cryptde.public_key().clone(), }) @@ -74,7 +103,7 @@ mod tests { use crate::bootstrapper::CryptDEPair; use crate::sub_lib::proxy_server::ProxyProtocol; use lazy_static::lazy_static; - use masq_lib::constants::HTTP_PORT; + use masq_lib::constants::{HTTP_PORT, TLS_PORT}; use masq_lib::test_utils::logging::init_test_logging; use masq_lib::test_utils::logging::TestLogHandler; use std::net::SocketAddr; @@ -85,14 +114,127 @@ mod tests { static ref CRYPTDE_PAIR: CryptDEPair = CryptDEPair::null(); } + #[test] + fn ibcd_hostname_overrides_supplied_hostname() { + let data = PlainData::new(&b"GET http://borkoed.com:1234/fleebs.html HTTP/1.1\r\n\r\n"[..]); + let ibcd = InboundClientData { + timestamp: SystemTime::now(), + client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), + reception_port_opt: Some(HTTP_PORT), + sequence_number_opt: Some(1), + last_data: false, + is_clandestine: false, + data: data.clone().into(), + }; + let cryptde = CRYPTDE_PAIR.main.dup(); + let stream_key = StreamKey::make_meaningless_stream_key(); + let logger = Logger::new("ibcd_hostname_overrides_supplied_hostname"); + let subject = Box::new(ClientRequestPayloadFactoryReal::new()); + + let result = subject + .make( + &ibcd, + stream_key, + Some(Host::new("ignored.com", 4321)), + cryptde.as_ref(), + &logger, + ) + .unwrap(); + + assert_eq!(result.target_hostname, String::from("borkoed.com")); + assert_eq!(result.target_port, 1234); + } + + #[test] + fn uses_supplied_host_if_ibcd_does_not_have_one() { + let test_name = "uses_supplied_hostname_if_ibcd_does_not_have_one"; + let data = PlainData::new(&[0x01, 0x02, 0x03]); // No host can be extracted here + let ibcd = InboundClientData { + timestamp: SystemTime::now(), + client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), + reception_port_opt: Some(HTTP_PORT), + sequence_number_opt: Some(1), + last_data: false, + is_clandestine: false, + data: data.into(), + }; + let cryptde = CRYPTDE_PAIR.main.dup(); + let stream_key = StreamKey::make_meaningful_stream_key(test_name); + let logger = Logger::new(test_name); + let subject = Box::new(ClientRequestPayloadFactoryReal::new()); + let supplied_host = Host::new("supplied.com", 4321); + + let result = subject + .make( + &ibcd, + stream_key, + Some(supplied_host.clone()), + cryptde.as_ref(), + &logger, + ) + .unwrap(); + + assert_eq!(result.target_hostname, supplied_host.name); + } + + #[test] + fn logs_error_and_returns_none_if_no_ibcd_host_and_no_supplied_host() { + init_test_logging(); + let test_name = "logs_error_and_returns_none_if_no_ibcd_hostname_and_no_supplied_hostname"; + let data = PlainData::new(&[0x01, 0x02, 0x03]); // no host can be extracted here + let ibcd = InboundClientData { + timestamp: SystemTime::now(), + client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), + reception_port_opt: Some(HTTP_PORT), + sequence_number_opt: Some(1), + last_data: false, + is_clandestine: false, + data: data.into(), + }; + let cryptde = CRYPTDE_PAIR.main.dup(); + let stream_key = StreamKey::make_meaningful_stream_key(test_name); + let logger = Logger::new(test_name); + let subject = Box::new(ClientRequestPayloadFactoryReal::new()); + + let result = subject.make(&ibcd, stream_key, None, cryptde.as_ref(), &logger); + + assert_eq!(result, None); + TestLogHandler::new().exists_log_containing(&format!("ERROR: {test_name}: No hostname information found in either client packet (Malformed HTTP request: '') or ProxyServer for protocol HTTP")); + } + + #[test] + fn logs_different_error_and_returns_none_if_connection_is_opened_and_immediately_closed() { + init_test_logging(); + let test_name = + "logs_different_error_and_returns_none_if_connection_is_opened_and_immediately_closed"; + let ibcd = InboundClientData { + timestamp: SystemTime::now(), + client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), + reception_port_opt: Some(HTTP_PORT), + sequence_number_opt: Some(1), + last_data: true, + is_clandestine: false, + data: vec![], + }; + let cryptde = CRYPTDE_PAIR.main.dup(); + let stream_key = StreamKey::make_meaningful_stream_key(test_name); + let logger = Logger::new(test_name); + let subject = Box::new(ClientRequestPayloadFactoryReal::new()); + + let result = subject.make(&ibcd, stream_key, None, cryptde.as_ref(), &logger); + + assert_eq!(result, None); + TestLogHandler::new().exists_log_containing(&format!("WARN: {test_name}: Client opened HTTP connection and immediately closed it without sending any data")); + } + #[test] fn handles_http_with_a_port() { let data = PlainData::new(&b"GET http://borkoed.com:2345/fleebs.html HTTP/1.1\r\n\r\n"[..]); let ibcd = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port: Some(HTTP_PORT), - sequence_number: Some(1), + reception_port_opt: Some(HTTP_PORT), + sequence_number_opt: Some(1), last_data: false, is_clandestine: false, data: data.clone().into(), @@ -102,7 +244,7 @@ mod tests { let logger = Logger::new("test"); let subject = Box::new(ClientRequestPayloadFactoryReal::new()); - let result = subject.make(&ibcd, stream_key, cryptde, &logger); + let result = subject.make(&ibcd, stream_key, None, cryptde, &logger); assert_eq!( result, @@ -113,7 +255,7 @@ mod tests { sequence_number: 1, last_data: false }, - target_hostname: Some(String::from("borkoed.com")), + target_hostname: String::from("borkoed.com"), target_port: 2345, protocol: ProxyProtocol::HTTP, originator_public_key: cryptde.public_key().clone(), @@ -128,8 +270,8 @@ mod tests { let ibcd = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port: Some(HTTP_PORT), - sequence_number: Some(1), + reception_port_opt: Some(HTTP_PORT), + sequence_number_opt: Some(1), last_data: false, is_clandestine: false, data: data.clone().into(), @@ -139,7 +281,7 @@ mod tests { let stream_key = StreamKey::make_meaningful_stream_key(test_name); let subject = Box::new(ClientRequestPayloadFactoryReal::new()); - let result = subject.make(&ibcd, stream_key, cryptde, &logger); + let result = subject.make(&ibcd, stream_key, None, cryptde, &logger); assert_eq!( result, @@ -150,7 +292,7 @@ mod tests { sequence_number: 1, last_data: false }, - target_hostname: Some(String::from("borkoed.com")), + target_hostname: String::from("borkoed.com"), target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: cryptde.public_key().clone(), @@ -183,8 +325,8 @@ mod tests { let ibcd = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - sequence_number: Some(0), - reception_port: Some(443), + sequence_number_opt: Some(0), + reception_port_opt: Some(443), last_data: false, is_clandestine: false, data: data.clone().into(), @@ -194,7 +336,7 @@ mod tests { let logger = Logger::new("test"); let subject = Box::new(ClientRequestPayloadFactoryReal::new()); - let result = subject.make(&ibcd, stream_key, cryptde, &logger); + let result = subject.make(&ibcd, stream_key, None, cryptde, &logger); assert_eq!( result, @@ -205,8 +347,8 @@ mod tests { sequence_number: 0, last_data: false }, - target_hostname: Some(String::from("server.com")), - target_port: 443, + target_hostname: String::from("server.com"), + target_port: TLS_PORT, protocol: ProxyProtocol::TLS, originator_public_key: cryptde.public_key().clone(), }) @@ -215,6 +357,7 @@ mod tests { #[test] fn handles_tls_without_hostname() { + init_test_logging(); let test_name = "handles_tls_without_hostname"; let data = PlainData::new(&[ 0x16, // content_type: Handshake @@ -233,10 +376,10 @@ mod tests { let ibcd = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port: Some(443), + reception_port_opt: Some(443), last_data: true, is_clandestine: false, - sequence_number: Some(0), + sequence_number_opt: Some(0), data: data.clone().into(), }; let cryptde = CRYPTDE_PAIR.main.as_ref(); @@ -244,23 +387,10 @@ mod tests { let stream_key = StreamKey::make_meaningful_stream_key(test_name); let subject = Box::new(ClientRequestPayloadFactoryReal::new()); - let result = subject.make(&ibcd, stream_key, cryptde, &logger); + let result = subject.make(&ibcd, stream_key, None, cryptde, &logger); - assert_eq!( - result, - Some(ClientRequestPayload_0v1 { - stream_key, - sequenced_packet: SequencedPacket { - data: data.into(), - sequence_number: 0, - last_data: true - }, - target_hostname: None, - target_port: 443, - protocol: ProxyProtocol::TLS, - originator_public_key: cryptde.public_key().clone(), - }) - ); + assert_eq!(result, None); + TestLogHandler::new().exists_log_containing(&format!("ERROR: {test_name}: No hostname information found in either client packet (ClientHello with no SNI extension) or ProxyServer for protocol TLS")); } #[test] @@ -270,8 +400,8 @@ mod tests { let ibcd = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - sequence_number: Some(0), - reception_port: None, + sequence_number_opt: Some(0), + reception_port_opt: None, last_data: false, is_clandestine: false, data: vec![0x10, 0x11, 0x12], @@ -281,7 +411,7 @@ mod tests { let stream_key = StreamKey::make_meaningful_stream_key(test_name); let subject = Box::new(ClientRequestPayloadFactoryReal::new()); - let result = subject.make(&ibcd, stream_key, cryptde, &logger); + let result = subject.make(&ibcd, stream_key, None, cryptde, &logger); assert_eq!(result, None); TestLogHandler::new().exists_log_containing( @@ -296,8 +426,8 @@ mod tests { let ibcd = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port: Some(1234), - sequence_number: Some(0), + reception_port_opt: Some(1234), + sequence_number_opt: Some(0), last_data: false, is_clandestine: true, data: vec![0x10, 0x11, 0x12], @@ -307,7 +437,7 @@ mod tests { let stream_key = StreamKey::make_meaningful_stream_key(test_name); let subject = Box::new(ClientRequestPayloadFactoryReal::new()); - let result = subject.make(&ibcd, stream_key, cryptde, &logger); + let result = subject.make(&ibcd, stream_key, None, cryptde, &logger); assert_eq!(result, None); TestLogHandler::new().exists_log_containing(&format!("ERROR: {test_name}: No protocol associated with origin port 1234 for 3-byte non-clandestine packet: [16, 17, 18]")); @@ -315,13 +445,14 @@ mod tests { #[test] fn use_sequence_from_inbound_client_data_in_client_request_payload() { + let data = PlainData::new(&b"GET http://borkoed.com/fleebs.html HTTP/1.1\r\n\r\n"[..]); let ibcd = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:80").unwrap(), - reception_port: Some(HTTP_PORT), - sequence_number: Some(1), + reception_port_opt: Some(HTTP_PORT), + sequence_number_opt: Some(1), last_data: false, - data: vec![0x10, 0x11, 0x12], + data: data.into(), is_clandestine: false, }; let cryptde = CRYPTDE_PAIR.main.as_ref(); @@ -332,6 +463,7 @@ mod tests { .make( &ibcd, StreamKey::make_meaningless_stream_key(), + None, cryptde, &logger, ) @@ -344,25 +476,26 @@ mod tests { fn makes_no_payload_if_sequence_number_is_unknown() { init_test_logging(); let test_name = "makes_no_payload_if_sequence_number_is_unknown"; + let data = PlainData::new(&b"GET http://borkoed.com/fleebs.html HTTP/1.1\r\n\r\n"[..]); let ibcd = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:80").unwrap(), - reception_port: Some(HTTP_PORT), + reception_port_opt: Some(HTTP_PORT), last_data: false, is_clandestine: false, - sequence_number: None, - data: vec![1, 3, 5, 7], + sequence_number_opt: None, + data: data.into(), }; let cryptde = CRYPTDE_PAIR.main.as_ref(); let logger = Logger::new(test_name); let stream_key = StreamKey::make_meaningful_stream_key(test_name); let subject = Box::new(ClientRequestPayloadFactoryReal::new()); - let result = subject.make(&ibcd, stream_key, cryptde, &logger); + let result = subject.make(&ibcd, stream_key, None, cryptde, &logger); assert_eq!(result, None); TestLogHandler::new().exists_log_containing(&format!( - "ERROR: {test_name}: internal error: got IBCD with no sequence number and 4 bytes" + "ERROR: {test_name}: internal error: got IBCD with no sequence number and 47 bytes" )); } } diff --git a/node/src/proxy_server/http_protocol_pack.rs b/node/src/proxy_server/http_protocol_pack.rs index b611f2be3..ef8940c6d 100644 --- a/node/src/proxy_server/http_protocol_pack.rs +++ b/node/src/proxy_server/http_protocol_pack.rs @@ -1,7 +1,8 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::proxy_server::protocol_pack::{Host, ProtocolPack, ServerImpersonator}; +use crate::proxy_server::protocol_pack::{ProtocolPack, ServerImpersonator}; use crate::proxy_server::server_impersonator_http::ServerImpersonatorHttp; use crate::sub_lib::cryptde::PlainData; +use crate::sub_lib::host::Host; use crate::sub_lib::proxy_server::ProxyProtocol; use lazy_static::lazy_static; use masq_lib::constants::HTTP_PORT; @@ -12,6 +13,7 @@ lazy_static! { static ref HOST_PATTERN: Regex = Regex::new(r"^(?:https?://)?([^\s/]+)").expect("bad regex"); } +#[derive(Clone, Copy)] pub struct HttpProtocolPack {} impl ProtocolPack for HttpProtocolPack { @@ -33,6 +35,14 @@ impl ProtocolPack for HttpProtocolPack { fn server_impersonator(&self) -> Box { Box::new(ServerImpersonatorHttp {}) } + + fn describe_packet(&self, data: &PlainData) -> String { + if data.as_slice().starts_with(b"HTTP/") { + self.describe_response(data) + } else { + self.describe_request(data) + } + } } impl HttpProtocolPack { @@ -100,6 +110,82 @@ impl HttpProtocolPack { Ok(port) => Ok(port), } } + + fn describe_request(&self, data: &PlainData) -> String { + let first_line_end = data + .as_slice() + .iter() + .position(|&b| b == b'\r') + .unwrap_or(data.len()); + let first_line = &data.as_slice()[0..first_line_end]; + let mut parts = first_line.split(|&b| b == b' '); + if let (Some(method_bytes), Some(path_bytes), Some(http_version_bytes)) = + (parts.next(), parts.next(), parts.next()) + { + let method = Self::from_utf8(method_bytes); + let path = Self::from_utf8(path_bytes); + let http_version = Self::from_utf8(http_version_bytes); + if let Some(host) = self.find_host(data) { + return if path.starts_with('/') { + format!( + "{} {} request to {}{}", + http_version, method, host.name, path + ) + } else { + format!("{} {} request to {}", http_version, method, path) + }; + } else { + return format!( + "{} {} request to unknown host: {}", + http_version, method, path + ); + } + } + format!( + "Malformed HTTP request: {}", + Self::truncate_data_as_string(data, 50) + ) + } + + fn describe_response(&self, data: &PlainData) -> String { + let first_line_end = data + .as_slice() + .iter() + .position(|&b| b == b'\r') + .unwrap_or(data.as_slice().len()); + let first_line = &data.as_slice()[0..first_line_end]; + let mut parts = first_line.split(|&b| b == b' '); + if let (Some(http_version_bytes), Some(status_code_bytes), Some(status_text_bytes)) = + (parts.next(), parts.next(), parts.next()) + { + let http_with_version = Self::from_utf8(http_version_bytes); + let status_code = Self::from_utf8(status_code_bytes); + let status_text = Self::from_utf8(status_text_bytes); + return format!( + "{} response with status {} {}", + http_with_version, status_code, status_text + ); + } + format!( + "Malformed HTTP response: {}", + Self::truncate_data_as_string(data, 50) + ) + } + + fn truncate_data_as_string(data: &PlainData, truncate_at: usize) -> String { + if data.as_slice().len() > truncate_at { + format!( + "'{}'...", + String::from_utf8_lossy(&data.as_slice()[0..truncate_at]) + ) + } else { + format!("'{}'", String::from_utf8_lossy(data.as_slice())) + } + } + + fn from_utf8(bytes: &[u8]) -> String { + String::from_utf8_lossy(bytes).to_string() + } } #[cfg(test)] @@ -336,4 +422,83 @@ mod tests { let data = b"CONNECTX"; assert!(!HttpProtocolPack::is_connect(data)); } + + #[test] + fn describe_packet_works_on_get_request_with_header_host() { + let data = + PlainData::new(b"GET /index.html?item=booga HTTP/1.1\r\nHost: www.example.com\r\n\r\n"); + let subject = HttpProtocolPack {}; + + let result = subject.describe_packet(&data); + + assert_eq!( + result, + "HTTP/1.1 GET request to www.example.com/index.html?item=booga" + ); + } + + #[test] + fn describe_packet_works_on_post_request_with_url_host() { + let data = PlainData::new( + b"POST www.example.com/person/1234 HTTP/1.1\r\nContent-Length: 2\r\n\r\n{}", + ); + let subject = HttpProtocolPack {}; + + let result = subject.describe_packet(&data); + + assert_eq!( + result, + "HTTP/1.1 POST request to www.example.com/person/1234" + ); + } + + #[test] + fn describe_packet_works_on_unexpected_request_with_unspecified_host() { + let data = PlainData::new(b"BOOGA /person/1234 HTTP/1.1\r\nContent-Length: 2\r\n\r\n{}"); + let subject = HttpProtocolPack {}; + + let result = subject.describe_packet(&data); + + assert_eq!( + result, + "HTTP/1.1 BOOGA request to unknown host: /person/1234" + ); + } + + #[test] + fn describe_packet_works_on_malformed_request() { + let data = PlainData::new(b"Fourscore_and_seven_years_ago_our_fathers_brought_forth_on_this_continent_a_new_nation,_conceived_in_liberty_and_dedicated_to_the_proposition_that_all_men_are_created_equal."); + let subject = HttpProtocolPack {}; + + let result = subject.describe_packet(&data); + + assert_eq!( + result, + "Malformed HTTP request: 'Fourscore_and_seven_years_ago_our_fathers_brought_'..." + ); + } + + #[test] + fn describe_packet_works_on_200_response() { + let data = + PlainData::new(b"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n"); + let subject = HttpProtocolPack {}; + + let result = subject.describe_packet(&data); + + assert_eq!(result, "HTTP/1.1 response with status 200 OK"); + } + + #[test] + fn describe_packet_works_on_malformed_response() { + let data = PlainData::new(b"HTTP/Fourscore_and_seven_years_ago_our_fathers_brought_forth_on_this_continent_a_new_nation,_conceived_in_liberty_and_dedicated_to_the_proposition_that_all_men_are_created_equal."); + let subject = HttpProtocolPack {}; + + let result = subject.describe_packet(&data); + + assert_eq!( + result, + "Malformed HTTP response: 'HTTP/Fourscore_and_seven_years_ago_our_fathers_bro'..." + ); + } } diff --git a/node/src/proxy_server/mod.rs b/node/src/proxy_server/mod.rs index 459acf668..68d7b1ebc 100644 --- a/node/src/proxy_server/mod.rs +++ b/node/src/proxy_server/mod.rs @@ -24,6 +24,7 @@ use crate::sub_lib::cryptde::PublicKey; use crate::sub_lib::dispatcher::InboundClientData; use crate::sub_lib::dispatcher::{Endpoint, StreamShutdownMsg}; use crate::sub_lib::hopper::{ExpiredCoresPackage, IncipientCoresPackage}; +use crate::sub_lib::host::Host; use crate::sub_lib::neighborhood::RouteQueryResponse; use crate::sub_lib::neighborhood::{ExpectedService, UpdateNodeRecordMetadataMessage}; use crate::sub_lib::neighborhood::{ExpectedServices, RatePack}; @@ -31,14 +32,13 @@ use crate::sub_lib::neighborhood::{NRMetadataChange, RouteQueryMessage}; use crate::sub_lib::peer_actors::BindMessage; use crate::sub_lib::proxy_client::{ClientResponsePayload_0v1, DnsResolveFailure_0v1}; use crate::sub_lib::proxy_server::ProxyServerSubs; -use crate::sub_lib::proxy_server::{AddReturnRouteMessage, StreamKeyPurge}; +use crate::sub_lib::proxy_server::StreamKeyPurge; use crate::sub_lib::proxy_server::{ AddRouteResultMessage, ClientRequestPayload_0v1, ProxyProtocol, }; use crate::sub_lib::route::Route; use crate::sub_lib::stream_handler_pool::TransmitDataMsg; use crate::sub_lib::stream_key::StreamKey; -use crate::sub_lib::ttl_hashmap::TtlHashMap; use crate::sub_lib::utils::{handle_ui_crash_request, MessageScheduler, NODE_MAILBOX_CAPACITY}; use crate::sub_lib::wallet::Wallet; use actix::Context; @@ -53,15 +53,13 @@ use masq_lib::utils::MutabilityConflictHelper; use regex::Regex; use std::collections::HashMap; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; -use std::rc::Rc; use std::str::FromStr; use std::time::{Duration, SystemTime}; use tokio::prelude::Future; pub const CRASH_KEY: &str = "PROXYSERVER"; -pub const RETURN_ROUTE_TTL: Duration = Duration::from_secs(120); - pub const STREAM_KEY_PURGE_DELAY: Duration = Duration::from_secs(30); +pub const DNS_FAILURE_RETRIES: usize = 3; struct ProxyServerOutSubs { dispatcher: Recipient, @@ -69,27 +67,31 @@ struct ProxyServerOutSubs { accountant: Recipient, route_source: Recipient, update_node_record_metadata: Recipient, - add_return_route: Recipient, stream_shutdown_sub: Recipient, route_result_sub: Recipient, schedule_stream_key_purge: Recipient>, } +#[derive(Clone, Debug)] +struct StreamInfo { + tunneled_host_opt: Option, + dns_failure_retry_opt: Option, + route_opt: Option, + protocol_opt: Option, + time_to_live_opt: Option, +} + pub struct ProxyServer { subs: Option, client_request_payload_factory: Box, stream_key_factory: Box, keys_and_addrs: BidiHashMap, - tunneled_hosts: HashMap, - dns_failure_retries: HashMap, - stream_key_routes: HashMap, - stream_key_ttl: HashMap, + stream_info: HashMap, is_decentralized: bool, consuming_wallet_balance: Option, cryptde_pair: CryptDEPair, crashable: bool, logger: Logger, - route_ids_to_return_routes: TtlHashMap, browser_proxy_sequence_offset: bool, inbound_client_data_helper_opt: Option>, stream_key_purge_delay: Duration, @@ -111,7 +113,6 @@ impl Handler for ProxyServer { 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, stream_shutdown_sub: msg.peer_actors.proxy_server.stream_shutdown_sub, route_result_sub: msg.peer_actors.proxy_server.route_result_sub, schedule_stream_key_purge: msg.peer_actors.proxy_server.schedule_stream_key_purge, @@ -128,64 +129,19 @@ impl Handler for ProxyServer { self.tls_connect(&msg); self.browser_proxy_sequence_offset = true; } else if let Err(e) = - self.help(|helper, proxy| helper.handle_normal_client_data(proxy, msg, false)) + // NOTE: I removed a 'false' parameter here for retire_stream_key because I think it was wrong. + self.help(|helper, proxy| helper.handle_normal_client_data(proxy, msg)) { error!(self.logger, "{}", e) } } } -impl Handler for ProxyServer { - type Result = (); - - fn handle(&mut self, msg: AddReturnRouteMessage, _ctx: &mut Self::Context) -> Self::Result { - self.route_ids_to_return_routes - .insert(msg.return_route_id, msg); - } -} - -impl AddReturnRouteMessage { - pub fn find_exit_node_key(&self) -> Option<&PublicKey> { - self.expected_services - .iter() - .find_map(|service| match service { - ExpectedService::Exit(public_key, _, _) => Some(public_key), - _ => None, - }) - } - - pub fn is_zero_hop(&self) -> bool { - self.expected_services == vec![ExpectedService::Nothing, ExpectedService::Nothing] - } -} - impl Handler for ProxyServer { type Result = (); fn handle(&mut self, msg: AddRouteResultMessage, _ctx: &mut Self::Context) -> Self::Result { - let dns_failure = self - .dns_failure_retries - .get(&msg.stream_key) - .unwrap_or_else(|| { - panic!("AddRouteResultMessage Handler: stream key: {} not found within dns_failure_retries", msg.stream_key); - }); - - match msg.result { - Ok(route_query_response) => { - debug!( - self.logger, - "Found a new route for hostname: {:?} - stream key: {} retries left: {}", - dns_failure.unsuccessful_request.target_hostname, - msg.stream_key, - dns_failure.retries_left - ); - self.stream_key_routes - .insert(msg.stream_key, route_query_response); - } - Err(e) => { - warning!(self.logger, "No route found for hostname: {:?} - stream key {} - retries left: {} - AddRouteResultMessage Error: {}",dns_failure.unsuccessful_request.target_hostname, msg.stream_key, dns_failure.retries_left, e); - } - } + self.handle_add_route_result_message(msg) } } @@ -256,21 +212,18 @@ impl ProxyServer { crashable: bool, is_running_in_integration_test: bool, ) -> ProxyServer { + let ps_logger = Logger::new("ProxyServer"); ProxyServer { subs: None, client_request_payload_factory: Box::new(ClientRequestPayloadFactoryReal::new()), stream_key_factory: Box::new(StreamKeyFactoryReal {}), keys_and_addrs: BidiHashMap::new(), - tunneled_hosts: HashMap::new(), - dns_failure_retries: HashMap::new(), - stream_key_routes: HashMap::new(), - stream_key_ttl: HashMap::new(), + stream_info: HashMap::new(), is_decentralized, consuming_wallet_balance, cryptde_pair, crashable, - logger: Logger::new("ProxyServer"), - route_ids_to_return_routes: TtlHashMap::new(RETURN_ROUTE_TTL), + logger: ps_logger, browser_proxy_sequence_offset: false, inbound_client_data_helper_opt: Some(Box::new(IBCDHelperReal::new())), stream_key_purge_delay: STREAM_KEY_PURGE_DELAY, @@ -284,7 +237,6 @@ impl ProxyServer { from_dispatcher: recipient!(addr, InboundClientData), from_hopper: recipient!(addr, ExpiredCoresPackage), dns_failure_from_hopper: recipient!(addr, ExpiredCoresPackage), - add_return_route: recipient!(addr, AddReturnRouteMessage), stream_shutdown_sub: recipient!(addr, StreamShutdownMsg), node_from_ui: recipient!(addr, NodeFromUiMessage), route_result_sub: recipient!(addr, AddRouteResultMessage), @@ -292,24 +244,46 @@ impl ProxyServer { } } + fn stream_info(&self, stream_key: &StreamKey) -> Option<&StreamInfo> { + match self.stream_info.get(stream_key) { + None => { + error!( + self.logger, + "Stream key {} not found in stream_info", stream_key + ); + None + } + Some(info) => Some(info), + } + } + + fn stream_info_mut(&mut self, stream_key: &StreamKey) -> Option<&mut StreamInfo> { + match self.stream_info.get_mut(stream_key) { + None => { + error!( + self.logger, + "Stream key {} not found in stream_info", stream_key + ); + None + } + Some(info) => Some(info), + } + } + fn remove_dns_failure_retry( - &mut self, + stream_info: &mut StreamInfo, stream_key: &StreamKey, ) -> Result { - match self.dns_failure_retries.remove(stream_key) { + match stream_info.dns_failure_retry_opt.take() { None => Err(format!( - "No entry found inside dns_failure_retries hashmap for the stream_key: {:?}", + "No DNSFailureRetry entry found for the stream_key: {:?}", stream_key )), Some(retry) => Ok(retry), } } - fn retry_dns_resolution( - &mut self, - retry: DNSFailureRetry, - client_addr: SocketAddr, - ) -> DNSFailureRetry { + fn retry_dns_resolution(&mut self, retry: &DNSFailureRetry, client_addr: SocketAddr) { let args = TransmitToHopperArgs::new( self, retry.unsuccessful_request.clone(), @@ -317,7 +291,6 @@ impl ProxyServer { SystemTime::now(), false, ); - let add_return_route_sub = self.out_subs("ProxyServer").add_return_route.clone(); let route_source = self.out_subs("Neighborhood").route_source.clone(); let proxy_server_sub = self.out_subs("ProxyServer").route_result_sub.clone(); let inbound_client_data_helper = self @@ -325,13 +298,7 @@ impl ProxyServer { .as_ref() .expect("IBCDHelper uninitialized"); - inbound_client_data_helper.request_route_and_transmit( - args, - add_return_route_sub, - route_source, - proxy_server_sub, - ); - retry + inbound_client_data_helper.request_route_and_transmit(args, route_source, proxy_server_sub); } fn retire_stream_key(&mut self, stream_key: &StreamKey) { @@ -342,7 +309,7 @@ impl ProxyServer { &self, client_addr: SocketAddr, proxy_protocol: ProxyProtocol, - hostname_opt: Option, + hostname: String, ) { self.subs .as_ref() @@ -351,119 +318,261 @@ impl ProxyServer { .try_send(TransmitDataMsg { endpoint: Endpoint::Socket(client_addr), last_data: true, - sequence_number: Some(0), // DNS resolution errors always happen on the first request + sequence_number_opt: Some(0), // DNS resolution errors always happen on the first request data: from_protocol(proxy_protocol) .server_impersonator() - .dns_resolution_failure_response(hostname_opt), + .dns_resolution_failure_response(hostname), }) .expect("Dispatcher is dead"); } - fn handle_dns_resolve_failure(&mut self, msg: &ExpiredCoresPackage) { - let return_route_info = - match self.get_return_route_info(&msg.remaining_route, "dns resolve failure") { - Some(rri) => rri, - None => return, // TODO: Eventually we'll have to do something better here, but we'll probably need some heuristics. - }; - let exit_public_key = { - // ugly, ugly - let self_public_key = self.cryptde_pair.main.as_ref().public_key(); - return_route_info - .find_exit_node_key() - .unwrap_or_else(|| { - if !self.is_decentralized { - self_public_key - } else { - panic!( - "Internal error: return_route_info for {} has no exit Node", - return_route_info.return_route_id + fn get_response_services( + route_query_response: &RouteQueryResponse, + ) -> Option<&[ExpectedService]> { + match &route_query_response.expected_services { + ExpectedServices::RoundTrip(_, back) => Some(back), + _ => None, + } + } + + fn find_exit_node_key(response_services: &[ExpectedService]) -> Option { + response_services + .iter() + .find_map(|service| service.exit_node_key_opt()) + } + + fn handle_add_route_result_message(&mut self, msg: AddRouteResultMessage) { + // We can't access self.logger for logging once we obtain mutable access to a stream_info + // element. So we create a delayed_log closure that we can call with self.logger after + // we've finished with the mutable borrow. We have to use #[allow(unused_assignments)] + // because Rust can't figure out that delayed_log will always be assigned before it's used. + type DelayedLogArgs = Box; + #[allow(unused_assignments)] + let mut delayed_log: DelayedLogArgs = Box::new(|_, _, _, _, _| {}); + let (target_hostname, stream_key, retries_left, message) = { + let mut stream_info = self.stream_info_mut(&msg.stream_key).unwrap_or_else(|| { + panic!( + "AddRouteResultMessage Handler: stream key: {} not found", + msg.stream_key + ) + }); + let dns_failure_retry = stream_info + .dns_failure_retry_opt + .as_ref() + .unwrap_or_else(|| + panic!("AddRouteResultMessage Handler: dns_failure_retry_opt is None for stream key {}", msg.stream_key) + ); + let mut message = String::new(); + match msg.result { + Ok(route_query_response) => { + delayed_log = Box::new( + move |logger: &Logger, + target_hostname: String, + stream_key: StreamKey, + retries_left: usize, + _: String| { + debug!( + logger, + "Found a new route for hostname: {:?} - stream key: {} retries left: {}", + target_hostname, + stream_key, + retries_left ); - } - }) - .clone() + }, + ); + stream_info.route_opt = Some(route_query_response); + } + Err(e) => { + message = e; + delayed_log = Box::new( + move |logger: &Logger, + target_hostname: String, + stream_key: StreamKey, + retries_left: usize, + message: String| { + warning!( + logger, + "No route found for hostname: {:?} - stream key {} - retries left: {} - AddRouteResultMessage Error: {}", + target_hostname, + stream_key, + retries_left, + message + ); + }, + ); + } + } + ( + dns_failure_retry + .unsuccessful_request + .target_hostname + .clone(), + msg.stream_key, + dns_failure_retry.retries_left, + message, + ) }; + delayed_log( + &self.logger, + target_hostname, + stream_key, + retries_left, + message, + ); + } - let hostname_opt = return_route_info.hostname_opt.clone(); + fn handle_dns_resolve_failure(&mut self, msg: &ExpiredCoresPackage) { let response = &msg.payload; - match self.keys_and_addrs.a_to_b(&response.stream_key) { - Some(client_addr) => { - if let Some(server_name) = hostname_opt.clone() { - self.subs - .as_ref() - .expect("Neighborhood unbound in ProxyServer") - .update_node_record_metadata - .try_send(UpdateNodeRecordMetadataMessage { - public_key: exit_public_key, - metadata_change: NRMetadataChange::AddUnreachableHost { - hostname: server_name, - }, - }) - .expect("Neighborhood is dead"); - } else { + // The idea here is that the Borrow Checker will not allow us to modify the StreamInfo in + // ProxyServer's map while we're looking at one of its values. So we're making a mutable + // copy of the StreamInfo, modifying that as necessary, and then overwriting the original + // map element with the modified copy at the end of the function. However, under certain + // circumstances we want to _retire_ the stream key; so we have a restore_stream_info + // flag that starts out true and is set to false if we retire the stream key. It's an + // ugly hack. Thanks, Borrow Checker! + let mut stream_info = match self.stream_info.remove(&response.stream_key) { + Some(info) => info, + None => { + error!( + self.logger, + "Discarding DnsResolveFailure message from an unrecognized stream key {:?}", + &response.stream_key + ); + return; + } + }; + let mut restore_stream_info = true; + + let route_query_response = match &stream_info.route_opt { + Some(route_query_response) => route_query_response, + None => { + error!( + self.logger, + "Stream info for stream key {} has no route info", &response.stream_key + ); + return; + } + }; + let response_services = match Self::get_response_services(route_query_response) { + Some(response_services) => response_services, + None => { + error!( + self.logger, + "Stream info for stream key {} has no response services in its route info", + &response.stream_key + ); + return; + } + }; + let exit_public_key = if !self.is_decentralized { + self.cryptde_pair.main.public_key().clone() + } else { + match Self::find_exit_node_key(response_services) { + Some(exit_public_key) => exit_public_key, + None => { error!( self.logger, - "Exit node {exit_public_key} complained of DNS failure, but was given no hostname to resolve." + "Stream info for stream key {} has no exit node in its response services", + &response.stream_key ); - // TODO: Malefactor ban the exit node because it lied about the DNS failure. + return; } - self.report_response_services_consumed(&return_route_info, 0, msg.payload_len); - let retry = match self.remove_dns_failure_retry(&response.stream_key) { - Ok(retry) => retry, - Err(error_msg) => { - error!( - self.logger, - "While handling ExpiredCoresPackage: {}", error_msg + } + }; + let response = &msg.payload; + + match self.keys_and_addrs.a_to_b(&response.stream_key) { + Some(client_addr) => { + self.subs + .as_ref() + .expect("Neighborhood unbound in ProxyServer") + .update_node_record_metadata + .try_send(UpdateNodeRecordMetadataMessage { + public_key: exit_public_key, + metadata_change: NRMetadataChange::AddUnreachableHost { + hostname: route_query_response.host.name.clone(), + }, + }) + .expect("Neighborhood is dead"); + self.report_response_services_consumed(response_services, 0, msg.payload_len); + if let Some(retry_ref) = &mut stream_info.dns_failure_retry_opt { + debug!( + self.logger, + "Handling DNS failure for hostname {:?} - stream key: {} retries left: {}", + retry_ref.unsuccessful_request.target_hostname, + &response.stream_key, + retry_ref.retries_left + ); + if retry_ref.retries_left > 0 { + self.retry_dns_resolution(retry_ref, client_addr); + retry_ref.retries_left -= 1; + } else { + restore_stream_info = false; + self.retire_stream_key(&response.stream_key); + let protocol = stream_info.protocol_opt.expect( + "StreamInfo should always have a protocol_opt set by the time we get a DNS failure" + ); + self.send_dns_failure_response_to_the_browser( + client_addr, + protocol, + route_query_response.host.name.clone(), ); - return; } - }; - if retry.retries_left > 0 { - let mut returned_retry = self.retry_dns_resolution(retry, client_addr); - returned_retry.retries_left -= 1; - self.dns_failure_retries - .insert(response.stream_key, returned_retry); } else { - self.retire_stream_key(&response.stream_key); - self.send_dns_failure_response_to_the_browser( - client_addr, - return_route_info.protocol, - hostname_opt, + error!( + self.logger, + "While handling ExpiredCoresPackage: No DNSFailureRetry entry found for the stream_key: {:?}", + &response.stream_key ); + return; } } None => { error!(self.logger, "Discarding DnsResolveFailure message for {} from an unrecognized stream key {:?}", - hostname_opt.unwrap_or_else(|| "".to_string()), + route_query_response.host.name, &response.stream_key ) } } + if restore_stream_info { + self.stream_info.insert(response.stream_key, stream_info); + } } fn schedule_stream_key_purge(&mut self, stream_key: StreamKey) { - let host_info = match self.tunneled_hosts.get(&stream_key) { - None => String::from(""), - Some(hostname) => format!(", which was tunneling to the host {:?}", hostname), - }; - debug!( - self.logger, - "Client closed stream referenced by stream key {:?}{}. It will be purged after {:?}.", - &stream_key, - host_info, - self.stream_key_purge_delay - ); - self.stream_key_ttl.insert(stream_key, SystemTime::now()); - self.subs - .as_ref() - .expect("ProxyServer Subs Unbound") - .schedule_stream_key_purge - .try_send(MessageScheduler { - scheduled_msg: StreamKeyPurge { stream_key }, - delay: self.stream_key_purge_delay, - }) - .expect("ProxyServer is dead"); + let stream_key_purge_delay = self.stream_key_purge_delay; + // We can't access self.logger for logging once we obtain mutable access to a stream_info + // element. So we create a delayed_log closure that we can call with self.logger after + // we've finished with the mutable borrow. + let mut delayed_log: Box = Box::new(|_: &Logger| {}); + if let Some(stream_info) = self.stream_info_mut(&stream_key) { + let host_info = match &stream_info.tunneled_host_opt { + None => String::from(""), + Some(hostname) => format!(", which was tunneling to the host {:?}", hostname), + }; + delayed_log = Box::new(move |logger: &Logger| { + debug!( + logger, + "Client closed stream referenced by stream key {:?}{}. It will be purged after {:?}.", + &stream_key, + host_info, + stream_key_purge_delay + ); + }); + stream_info.time_to_live_opt = Some(SystemTime::now()); + self.subs + .as_ref() + .expect("ProxyServer Subs Unbound") + .schedule_stream_key_purge + .try_send(MessageScheduler { + scheduled_msg: StreamKeyPurge { stream_key }, + delay: self.stream_key_purge_delay, + }) + .expect("ProxyServer is dead"); + } + delayed_log(&self.logger); } fn log_straggling_packet( @@ -488,14 +597,6 @@ impl ProxyServer { &mut self, msg: ExpiredCoresPackage, ) { - debug!( - self.logger, - "ExpiredCoresPackage remaining_route: {}", - msg.remaining_route.to_string(vec![ - self.cryptde_pair.main.as_ref(), - self.cryptde_pair.main.as_ref() - ]) - ); let payload_data_len = msg.payload_len; let response = msg.payload; debug!( @@ -503,67 +604,69 @@ impl ProxyServer { "Relaying ClientResponsePayload (stream key {}, sequence {}, length {}) from Hopper to Dispatcher for client", response.stream_key, response.sequenced_packet.sequence_number, response.sequenced_packet.data.len() ); - let return_route_info = - match self.get_return_route_info(&msg.remaining_route, "client response") { - Some(rri) => rri, - None => return, - }; + let expected_services = match self.get_expected_return_services(&response.stream_key) { + Some(expected_services) => expected_services, + None => return, + }; self.report_response_services_consumed( - &return_route_info, + &expected_services, response.sequenced_packet.data.len(), payload_data_len, ); let stream_key = response.stream_key; - match self.remove_dns_failure_retry(&stream_key) { - Ok(_) => { - debug!(self.logger, "Successful attempt of DNS resolution, removing DNS retry entry for stream key: {}", &response.stream_key) - } - Err(_) => { + if let Some(info) = self.stream_info_mut(&stream_key) { + if let Err(e) = ProxyServer::remove_dns_failure_retry(info, &stream_key) { trace!( self.logger, - "No DNS retry entry found for stream key: {} during a successful attempt", - &response.stream_key - ) + "No DNS retry entry found for stream key {} during a successful attempt: {}", + &stream_key, + e + ); } } - if let Some(old_timestamp) = self.stream_key_ttl.get(&stream_key) { - self.log_straggling_packet(&stream_key, payload_data_len, old_timestamp) - } else { - match self.keys_and_addrs.a_to_b(&stream_key) { - Some(socket_addr) => { - let last_data = response.sequenced_packet.last_data; - let sequence_number = Some( - response.sequenced_packet.sequence_number - + self.browser_proxy_sequence_offset as u64, - ); - self.subs - .as_ref() - .expect("Dispatcher unbound in ProxyServer") - .dispatcher - .try_send(TransmitDataMsg { - endpoint: Endpoint::Socket(socket_addr), - last_data, - sequence_number, - data: response.sequenced_packet.data, - }) - .expect("Dispatcher is dead"); - if last_data { - self.purge_stream_key(&stream_key, "last data received from the exit node"); + if let Some(info) = self.stream_info(&stream_key) { + if let Some(old_timestamp) = info.time_to_live_opt { + self.log_straggling_packet(&stream_key, payload_data_len, &old_timestamp) + } else { + match self.keys_and_addrs.a_to_b(&stream_key) { + Some(socket_addr) => { + let last_data = response.sequenced_packet.last_data; + let sequence_number_opt = Some( + response.sequenced_packet.sequence_number + + self.browser_proxy_sequence_offset as u64, + ); + self.subs + .as_ref() + .expect("Dispatcher unbound in ProxyServer") + .dispatcher + .try_send(TransmitDataMsg { + endpoint: Endpoint::Socket(socket_addr), + last_data, + sequence_number_opt, + data: response.sequenced_packet.data, + }) + .expect("Dispatcher is dead"); + if last_data { + self.purge_stream_key( + &stream_key, + "last data received from the exit node", + ); + } + } + None => { + // TODO GH-608: It would be really nice to be able to send an InboundClientData with last_data: true + // back to the ProxyClient (and the distant server) so that the server could shut down + // its stream, since the browser has shut down _its_ stream and no more data will + // ever be accepted from the server on that stream; but we don't have enough information + // to do so, since our stream key has been purged and all the information it keyed + // is gone. Sorry, server! + warning!(self.logger, + "Discarding {}-byte packet {} from an unrecognized stream key: {:?}; can't send response back to client", + response.sequenced_packet.data.len(), + response.sequenced_packet.sequence_number, + response.stream_key, + ) } - } - None => { - // TODO GH-608: It would be really nice to be able to send an InboundClientData with last_data: true - // back to the ProxyClient (and the distant server) so that the server could shut down - // its stream, since the browser has shut down _its_ stream and no more data will - // ever be accepted from the server on that stream; but we don't have enough information - // to do so, since our stream key has been purged and all the information it keyed - // is gone. Sorry, server! - warning!(self.logger, - "Discarding {}-byte packet {} from an unrecognized stream key: {:?}; can't send response back to client", - response.sequenced_packet.data.len(), - response.sequenced_packet.sequence_number, - response.stream_key, - ) } } } @@ -574,7 +677,10 @@ impl ProxyServer { match http_data { Some(ref host) if host.port == TLS_PORT => { let stream_key = self.find_or_generate_stream_key(msg); - self.tunneled_hosts.insert(stream_key, host.name.clone()); + match self.stream_info_mut(&stream_key) { + None => return, + Some(stream_info) => stream_info.tunneled_host_opt = Some(host.name.clone()), + } self.subs .as_ref() .expect("Dispatcher unbound in ProxyServer") @@ -582,7 +688,7 @@ impl ProxyServer { .try_send(TransmitDataMsg { endpoint: Endpoint::Socket(msg.client_addr), last_data: false, - sequence_number: msg.sequence_number, + sequence_number_opt: msg.sequence_number_opt, data: b"HTTP/1.1 200 OK\r\n\r\n".to_vec(), }) .expect("Dispatcher is dead"); @@ -595,7 +701,7 @@ impl ProxyServer { .try_send(TransmitDataMsg { endpoint: Endpoint::Socket(msg.client_addr), last_data: true, - sequence_number: msg.sequence_number, + sequence_number_opt: msg.sequence_number_opt, data: b"HTTP/1.1 400 Bad Request\r\nContent-Length: 0\r\n\r\n".to_vec(), }) .expect("Dispatcher is dead"); @@ -636,14 +742,13 @@ impl ProxyServer { let ibcd = InboundClientData { timestamp: SystemTime::now(), client_addr: msg.peer_addr, - reception_port: Some(nca.reception_port), + reception_port_opt: Some(nca.reception_port), last_data: true, is_clandestine: false, - sequence_number: Some(nca.sequence_number), + sequence_number_opt: Some(nca.sequence_number), data: vec![], }; - if let Err(e) = - self.help(|helper, proxy| helper.handle_normal_client_data(proxy, ibcd, true)) + if let Err(e) = self.help(|helper, proxy| helper.handle_normal_client_data(proxy, ibcd)) { error!(self.logger, "{}", e) }; @@ -667,6 +772,16 @@ impl ProxyServer { ibcd.client_addr, ); self.keys_and_addrs.insert(stream_key, ibcd.client_addr); + self.stream_info.insert( + stream_key, + StreamInfo { + tunneled_host_opt: None, + dns_failure_retry_opt: None, + route_opt: None, + protocol_opt: None, + time_to_live_opt: None, + }, + ); debug!( self.logger, "find_or_generate_stream_key() inserted new key {} for {}", @@ -684,9 +799,7 @@ impl ProxyServer { "Retiring stream key {} due to {}", &stream_key, reason ); let _ = self.keys_and_addrs.remove_a(stream_key); - let _ = self.stream_key_routes.remove(stream_key); - let _ = self.tunneled_hosts.remove(stream_key); - let _ = self.stream_key_ttl.remove(stream_key); + let _ = self.stream_info.remove(stream_key); } fn make_payload( @@ -694,10 +807,17 @@ impl ProxyServer { ibcd: InboundClientData, stream_key: &StreamKey, ) -> Result { - let tunnelled_host = self.tunneled_hosts.get(stream_key); - let new_ibcd = match tunnelled_host { + let stream_info_opt = self.stream_info.get(stream_key); + let (host_opt, tunnelled_host_opt) = match stream_info_opt { + None => (None, None), + Some(info) => match &info.route_opt { + Some(route) => (Some(route.host.clone()), info.tunneled_host_opt.clone()), + None => (None, info.tunneled_host_opt.clone()), + }, + }; + let new_ibcd = match tunnelled_host_opt { Some(_) => InboundClientData { - reception_port: Some(443), + reception_port_opt: Some(TLS_PORT), ..ibcd }, None => ibcd, @@ -705,13 +825,14 @@ impl ProxyServer { match self.client_request_payload_factory.make( &new_ibcd, *stream_key, + host_opt, self.cryptde_pair.alias.as_ref(), &self.logger, ) { None => Err("Couldn't create ClientRequestPayload".to_string()), - Some(payload) => match tunnelled_host { + Some(payload) => match tunnelled_host_opt { Some(hostname) => Ok(ClientRequestPayload_0v1 { - target_hostname: Some(hostname.clone()), + target_hostname: hostname, ..payload }), None => Ok(payload), @@ -721,24 +842,10 @@ impl ProxyServer { fn try_transmit_to_hopper( args: TransmitToHopperArgs, - add_return_route_sub: Recipient, route_query_response: RouteQueryResponse, ) -> Result<(), String> { 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: args.payload.protocol, - hostname_opt: args.payload.target_hostname.clone(), - }; - debug!( - args.logger, - "Adding expectant return route info: {:?}", return_route_info - ); - add_return_route_sub - .try_send(return_route_info) - .expect("ProxyServer is dead"); + ExpectedServices::RoundTrip(over, _) => { ProxyServer::transmit_to_hopper(args, route_query_response.route, over) } _ => panic!("Expected RoundTrip ExpectedServices but got OneWay"), @@ -891,7 +998,7 @@ impl ProxyServer { source_addr: SocketAddr, dispatcher: &Recipient, ) -> String { - let target_hostname = ProxyServer::hostname(&payload); + let target_hostname = payload.target_hostname.clone(); let stream_key = payload.stream_key; ProxyServer::send_route_failure(payload, source_addr, dispatcher); format!( @@ -907,56 +1014,45 @@ impl ProxyServer { ) { let data = from_protocol(payload.protocol) .server_impersonator() - .route_query_failure_response(&ProxyServer::hostname(&payload)); + .route_query_failure_response(&payload.target_hostname); let msg = TransmitDataMsg { endpoint: Endpoint::Socket(source_addr), last_data: true, - sequence_number: Some(0), + sequence_number_opt: Some(0), data, }; dispatcher.try_send(msg).expect("Dispatcher is dead"); } - fn hostname(payload: &ClientRequestPayload_0v1) -> String { - match payload.target_hostname { - Some(ref thn) => thn.clone(), - None => "".to_string(), - } - } - - fn get_return_route_info( - &self, - remaining_route: &Route, - source: &str, - ) -> Option> { - let mut mut_remaining_route = remaining_route.clone(); - mut_remaining_route - .shift(self.cryptde_pair.main.as_ref()) - .expect("Internal error: remaining route in ProxyServer with no hops"); - let return_route_id = match mut_remaining_route.id(self.cryptde_pair.main.as_ref()) { - Ok(rri) => rri, - Err(e) => { - error!(self.logger, "Can't report services consumed: {}", e); - return None; - } - }; - match self.route_ids_to_return_routes.get(&return_route_id) { - Some(rri) => Some(rri), + fn get_expected_return_services( + &mut self, + stream_key: &StreamKey, + ) -> Option> { + match self.stream_info(stream_key) { None => { - error!(self.logger, "Can't report services consumed: received response with bogus return-route ID {} for {}. Ignoring", return_route_id, source); + error!(self.logger, "Can't pay for return services consumed: received response with unrecognized stream key {:?}. Ignoring", stream_key); None } + Some(stream_info) => { + let route = stream_info + .route_opt + .as_ref() + .unwrap_or_else(|| panic!("Internal error: Request was sent over stream {:?} without an associated route being stored in stream_info: can't pay", stream_key)); + match route.expected_services { + ExpectedServices::RoundTrip(_, ref return_services) => Some(return_services.clone()), + _ => panic!("Internal error: ExpectedServices in ProxyServer for stream key {:?} is not RoundTrip", stream_key), + } + } } } fn report_response_services_consumed( &self, - return_route_info: &AddReturnRouteMessage, + expected_services: &[ExpectedService], exit_size: usize, routing_size: usize, ) { - let exit_service_report: ExitServiceSearch = return_route_info - .expected_services + let exit_service_report: ExitServiceSearch = expected_services .iter() .filter(|service| !matches!(service, ExpectedService::Nothing)) .fold(ZeroHop, |acc, service| { @@ -982,8 +1078,7 @@ impl ProxyServer { ZeroHop => return, Definite(report) => report, }; - let routing_service_reports = return_route_info - .expected_services + let routing_service_reports = expected_services .iter() .flat_map(|service| match service { ExpectedService::Routing(_, wallet, rate_pack) => Some(RoutingServiceConsumed { @@ -1022,13 +1117,11 @@ pub trait IBCDHelper { &self, proxy_s: &mut ProxyServer, msg: InboundClientData, - retire_stream_key: bool, ) -> Result<(), String>; fn request_route_and_transmit( &self, args: TransmitToHopperArgs, - add_return_route_sub: Recipient, route_source: Recipient, proxy_server_sub: Recipient, ); @@ -1038,7 +1131,7 @@ trait RouteQueryResponseResolver: Send { fn resolve_message( &self, args: TransmitToHopperArgs, - add_return_route_sub: Recipient, + // add_return_route_sub: Recipient, proxy_server_sub: Recipient, route_result_opt: Result, MailboxError>, ); @@ -1049,18 +1142,13 @@ impl RouteQueryResponseResolver for RouteQueryResponseResolverReal { fn resolve_message( &self, args: TransmitToHopperArgs, - add_return_route_sub: Recipient, proxy_server_sub: Recipient, route_result_opt: Result, MailboxError>, ) { let stream_key = args.payload.stream_key; let result = match route_result_opt { Ok(Some(route_query_response)) => { - match ProxyServer::try_transmit_to_hopper( - args, - add_return_route_sub, - route_query_response.clone(), - ) { + match ProxyServer::try_transmit_to_hopper(args, route_query_response.clone()) { Ok(()) => Ok(route_query_response), Err(e) => Err(e), } @@ -1102,15 +1190,16 @@ impl IBCDHelperReal { } } } + impl IBCDHelper for IBCDHelperReal { fn handle_normal_client_data( &self, - proxy: &mut ProxyServer, + proxy_server: &mut ProxyServer, msg: InboundClientData, - retire_stream_key: bool, ) -> Result<(), String> { let client_addr = msg.client_addr; - if proxy.consuming_wallet_balance.is_none() && proxy.is_decentralized { + let last_data = msg.last_data; + if proxy_server.consuming_wallet_balance.is_none() && proxy_server.is_decentralized { let protocol_pack = match from_ibcd(&msg) { Err(e) => return Err(e), Ok(pp) => pp, @@ -1121,25 +1210,25 @@ impl IBCDHelper for IBCDHelperReal { let msg = TransmitDataMsg { endpoint: Endpoint::Socket(client_addr), last_data: true, - sequence_number: Some(0), + sequence_number_opt: Some(0), data, }; - proxy + proxy_server .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.find_or_generate_stream_key(&msg); + let stream_key = proxy_server.find_or_generate_stream_key(&msg); let timestamp = msg.timestamp; - let payload = match proxy.make_payload(msg, &stream_key) { + let payload = match proxy_server.make_payload(msg, &stream_key) { Ok(payload) => { - if !proxy.is_running_in_integration_test { - if let Some(hostname) = &payload.target_hostname { - if let Err(e) = Hostname::new(hostname).validate_non_loopback_host() { - return Err(format!("Request to wildcard IP detected - {} (Most likely because Blockchain Service URL is not set)", e)); - } + if !proxy_server.is_running_in_integration_test { + if let Err(e) = + Hostname::new(&payload.target_hostname).validate_non_loopback_host() + { + return Err(format!("Request to wildcard IP detected - {} (Most likely because Blockchain Service URL is not set)", e)); } } payload @@ -1147,38 +1236,47 @@ impl IBCDHelper for IBCDHelperReal { Err(e) => return Err(e), }; - if proxy.dns_failure_retries.get(&stream_key).is_none() { - let dns_failure_retry = DNSFailureRetry { - unsuccessful_request: payload.clone(), - retries_left: if proxy.is_decentralized { 3 } else { 0 }, - }; - proxy - .dns_failure_retries - .insert(stream_key, dns_failure_retry); + { + let is_decentralized = proxy_server.is_decentralized; + let mut stream_info = proxy_server + .stream_info_mut(&stream_key) + .unwrap_or_else(|| panic!("Stream key {} disappeared!", &stream_key)); + if stream_info.dns_failure_retry_opt.is_none() { + let dns_failure_retry = DNSFailureRetry { + unsuccessful_request: payload.clone(), + retries_left: if is_decentralized { + DNS_FAILURE_RETRIES + } else { + 0 + }, + }; + stream_info.dns_failure_retry_opt = Some(dns_failure_retry); + stream_info.protocol_opt = Some(payload.protocol); + } } let args = - TransmitToHopperArgs::new(proxy, payload, client_addr, timestamp, retire_stream_key); - let add_return_route_sub = proxy.out_subs("ProxysServer").add_return_route.clone(); + TransmitToHopperArgs::new(proxy_server, payload, client_addr, timestamp, last_data); let pld = &args.payload; - if let Some(route_query_response) = proxy.stream_key_routes.get(&pld.stream_key) { + let stream_info = proxy_server + .stream_info(&pld.stream_key) + .unwrap_or_else(|| panic!("Stream key {} disappeared!", &pld.stream_key)); + if let Some(route_query_response) = &stream_info.route_opt { debug!( - proxy.logger, + proxy_server.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(args, add_return_route_sub, route_query_response) + ProxyServer::try_transmit_to_hopper(args, route_query_response) } else { - let route_source = proxy.out_subs("Neighborhood").route_source.clone(); - let proxy_server_sub = proxy.out_subs("ProxyServer").route_result_sub.clone(); - self.request_route_and_transmit( - args, - add_return_route_sub, - route_source, - proxy_server_sub, - ); + let route_source = proxy_server.out_subs("Neighborhood").route_source.clone(); + let proxy_server_sub = proxy_server + .out_subs("ProxyServer") + .route_result_sub + .clone(); + self.request_route_and_transmit(args, route_source, proxy_server_sub); Ok(()) } } @@ -1186,12 +1284,11 @@ impl IBCDHelper for IBCDHelperReal { fn request_route_and_transmit( &self, args: TransmitToHopperArgs, - add_return_route_sub: Recipient, neighborhood_sub: Recipient, proxy_server_sub: Recipient, ) { let pld = &args.payload; - let hostname_opt = pld.target_hostname.clone(); + let host = Host::new(&pld.target_hostname, pld.target_port); let logger = args.logger.clone(); debug!( logger, @@ -1206,16 +1303,11 @@ impl IBCDHelper for IBCDHelperReal { tokio::spawn( neighborhood_sub .send(RouteQueryMessage::data_indefinite_route_request( - hostname_opt, + host, payload_size, )) .then(move |route_result| { - message_resolver.resolve_message( - args, - add_return_route_sub, - proxy_server_sub, - route_result, - ); + message_resolver.resolve_message(args, proxy_server_sub, route_result); Ok(()) }), ); @@ -1354,6 +1446,7 @@ impl Hostname { #[cfg(test)] mod tests { use super::*; + use crate::blockchain::bip32::Bip32EncryptionKeyProvider; use crate::bootstrapper::CryptDEPair; use crate::match_every_type_id; use crate::proxy_server::protocol_pack::ServerImpersonator; @@ -1367,6 +1460,7 @@ mod tests { use crate::sub_lib::dispatcher::Component; use crate::sub_lib::hop::LiveHop; use crate::sub_lib::hopper::MessageType; + use crate::sub_lib::host::Host; 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}; @@ -1375,7 +1469,6 @@ mod tests { use crate::sub_lib::route::Route; use crate::sub_lib::route::RouteSegment; use crate::sub_lib::sequence_buffer::SequencedPacket; - use crate::sub_lib::ttl_hashmap::TtlHashMap; use crate::sub_lib::versioned_data::VersionedData; use crate::test_utils::make_meaningless_route; use crate::test_utils::make_paying_wallet; @@ -1390,7 +1483,6 @@ mod tests { }; use crate::test_utils::zero_hop_route_response; use actix::System; - use crossbeam_channel::unbounded; use lazy_static::lazy_static; use masq_lib::constants::{HTTP_PORT, TLS_PORT}; use masq_lib::test_utils::logging::init_test_logging; @@ -1459,7 +1551,6 @@ mod tests { fn resolve_message( &self, args: TransmitToHopperArgs, - _add_return_route_sub: Recipient, _proxy_server_sub: Recipient, route_result: Result, MailboxError>, ) { @@ -1486,11 +1577,59 @@ mod tests { self } } + + struct StreamInfoBuilder { + product: StreamInfo, + } + + impl StreamInfoBuilder { + pub fn new() -> Self { + Self { + product: StreamInfo { + tunneled_host_opt: None, + dns_failure_retry_opt: None, + route_opt: None, + protocol_opt: None, + time_to_live_opt: None, + }, + } + } + + pub fn tunneled_host(mut self, host: &str) -> Self { + self.product.tunneled_host_opt = Some(host.to_string()); + self + } + + pub fn dns_failure_retry(mut self, retry: DNSFailureRetry) -> Self { + self.product.dns_failure_retry_opt = Some(retry); + self + } + + pub fn route(mut self, route: RouteQueryResponse) -> Self { + self.product.route_opt = Some(route); + self + } + + pub fn protocol(mut self, protocol: ProxyProtocol) -> Self { + self.product.protocol_opt = Some(protocol); + self + } + + pub fn time_to_live(mut self, ttl: SystemTime) -> Self { + self.product.time_to_live_opt = Some(ttl); + self + } + + pub fn build(self) -> StreamInfo { + self.product + } + } + #[test] fn constants_have_correct_values() { assert_eq!(CRASH_KEY, "PROXYSERVER"); - assert_eq!(RETURN_ROUTE_TTL, Duration::from_secs(120)); assert_eq!(STREAM_KEY_PURGE_DELAY, Duration::from_secs(30)); + assert_eq!(DNS_FAILURE_RETRIES, 3); } const STANDARD_CONSUMING_WALLET_BALANCE: i64 = 0; @@ -1504,7 +1643,6 @@ mod tests { accountant: recipient!(addr, ReportServicesConsumedMessage), route_source: recipient!(addr, RouteQueryMessage), update_node_record_metadata: recipient!(addr, UpdateNodeRecordMetadataMessage), - add_return_route: recipient!(addr, AddReturnRouteMessage), stream_shutdown_sub: recipient!(addr, StreamShutdownMsg), route_result_sub: recipient!(addr, AddRouteResultMessage), schedule_stream_key_purge: recipient!(addr, MessageScheduler), @@ -1548,16 +1686,9 @@ mod tests { } } - fn return_route_with_id(cryptde: &dyn CryptDE, return_route_id: u32) -> Route { - let cover_hop = make_cover_hop(cryptde); - let id_hop = cryptde - .encode( - &cryptde.public_key(), - &PlainData::from(serde_cbor::ser::to_vec(&return_route_id).unwrap()), - ) - .unwrap(); + fn return_route(cryptde: &dyn CryptDE) -> Route { Route { - hops: vec![cover_hop, id_hop], + hops: vec![make_cover_hop(cryptde)], } } @@ -1576,7 +1707,7 @@ mod tests { #[derive(Default)] struct IBCDHelperMock { - handle_normal_client_data_params: Arc>>, + handle_normal_client_data_params: Arc>>, handle_normal_client_data_results: RefCell>>, } @@ -1585,12 +1716,11 @@ mod tests { &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)); + .push(msg); self.handle_normal_client_data_results .borrow_mut() .remove(0) @@ -1599,7 +1729,6 @@ mod tests { fn request_route_and_transmit( &self, _args: TransmitToHopperArgs, - _add_return_route_sub: Recipient, _route_source: Recipient, _proxy_server_sub: Recipient, ) { @@ -1610,7 +1739,7 @@ mod tests { impl IBCDHelperMock { fn handle_normal_client_data_params( mut self, - params: &Arc>>, + params: &Arc>>, ) -> Self { self.handle_normal_client_data_params = params.clone(); self @@ -1625,47 +1754,125 @@ mod tests { } #[test] - fn proxy_server_receives_http_request_with_new_stream_key_from_dispatcher_then_sends_cores_package_to_hopper( - ) { - init_test_logging(); - let test_name = "proxy_server_receives_http_request_with_new_stream_key_from_dispatcher_then_sends_cores_package_to_hopper"; - let main_cryptde = CRYPTDE_PAIR.main.as_ref(); - let alias_cryptde = CRYPTDE_PAIR.alias.as_ref(); - 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 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(); + fn get_expected_services_produces_nothing_if_nothing_exists() { + let mut subject = ProxyServer::new( + CRYPTDE_PAIR.clone(), + true, + Some(STANDARD_CONSUMING_WALLET_BALANCE), + false, + false, + ); let stream_key = StreamKey::make_meaningless_stream_key(); - let expected_data = http_request.to_vec(); - let msg_from_dispatcher = InboundClientData { - timestamp: SystemTime::now(), - client_addr: socket_addr, - reception_port: Some(HTTP_PORT), - sequence_number: Some(0), - last_data: true, - is_clandestine: false, - data: expected_data.clone(), - }; - let expected_http_request = PlainData::new(http_request); - let route = Route { hops: vec![] }; - let expected_payload = ClientRequestPayload_0v1 { - stream_key: stream_key.clone(), - sequenced_packet: SequencedPacket { - data: expected_http_request.into(), + + let result = subject.get_expected_return_services(&stream_key); + + assert!( + result.is_none(), + "Expected no expected services, but got: {:?}", + result + ); + } + + #[test] + fn get_expected_services_produces_rri_when_it_exists() { + let mut subject = ProxyServer::new( + CRYPTDE_PAIR.clone(), + true, + Some(STANDARD_CONSUMING_WALLET_BALANCE), + false, + false, + ); + let exit_public_key = PublicKey::new(&b"exit key"[..]); + let stream_key = StreamKey::make_meaningless_stream_key(); + let back_services = vec![ExpectedService::Exit( + exit_public_key, + make_wallet("booga"), + rate_pack(1000), + )]; + let expected_services = ExpectedServices::RoundTrip(vec![], back_services.clone()); + subject.stream_info.insert( + stream_key.clone(), + StreamInfoBuilder::new() + .route(RouteQueryResponse { + route: make_meaningless_route(&CRYPTDE_PAIR), + expected_services: expected_services.clone(), + host: Host::new("booga.com", TLS_PORT), + }) + .protocol(ProxyProtocol::TLS) + .build(), + ); + + let result = subject.get_expected_return_services(&stream_key).unwrap(); + + assert_eq!(result, back_services); + } + + #[test] + #[should_panic( + expected = "Internal error: Request was sent over stream Y29uc3RhbnQgZm9yIHBhbmljIG0 without an associated route being stored in stream_info: can't pay" + )] + fn get_expected_services_panics_if_stream_info_exists_but_has_no_route() { + let mut subject = ProxyServer::new( + CRYPTDE_PAIR.clone(), + true, + Some(STANDARD_CONSUMING_WALLET_BALANCE), + false, + false, + ); + let stream_key = StreamKey::from_bytes(b"constant for panic message"); + subject.stream_info.insert( + stream_key.clone(), + StreamInfoBuilder::new() + // no route_opt: problem + .protocol(ProxyProtocol::TLS) + .build(), + ); + + let _ = subject.get_expected_return_services(&stream_key).unwrap(); + } + + #[test] + fn proxy_server_receives_http_request_with_new_stream_key_from_dispatcher_then_sends_cores_package_to_hopper( + ) { + init_test_logging(); + let test_name = "proxy_server_receives_http_request_with_new_stream_key_from_dispatcher_then_sends_cores_package_to_hopper"; + let main_cryptde = CRYPTDE_PAIR.main.as_ref(); + let alias_cryptde = CRYPTDE_PAIR.alias.as_ref(); + 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 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![], + ), + host: Host::new("booga.com", HTTP_PORT), + })); + 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 = StreamKey::make_meaningless_stream_key(); + let expected_data = http_request.to_vec(); + let msg_from_dispatcher = InboundClientData { + timestamp: SystemTime::now(), + client_addr: socket_addr, + reception_port_opt: Some(HTTP_PORT), + sequence_number_opt: Some(0), + last_data: true, + is_clandestine: false, + data: expected_data.clone(), + }; + let expected_http_request = PlainData::new(http_request); + let route = Route { hops: vec![] }; + let expected_payload = ClientRequestPayload_0v1 { + stream_key: stream_key.clone(), + sequenced_packet: SequencedPacket { + data: expected_http_request.into(), sequence_number: 0, last_data: true, }, - target_hostname: Some(String::from("nowhere.com")), + target_hostname: String::from("nowhere.com"), target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: alias_cryptde.public_key().clone(), @@ -1682,7 +1889,7 @@ mod tests { thread::spawn(move || { let stream_key_factory = StreamKeyFactoryMock::new() .make_parameters(&make_parameters_arc) - .make_result(stream_key); + .make_result(stream_key.clone()); let system = System::new(test_name); let mut subject = ProxyServer::new( CRYPTDE_PAIR.clone(), @@ -1704,6 +1911,13 @@ mod tests { subject_addr.try_send(msg_from_dispatcher).unwrap(); + subject_addr + .try_send(AssertionsMessage { + assertions: Box::new(move |proxy_server: &mut ProxyServer| { + assert!(proxy_server.stream_info.contains_key(&stream_key)); + }), + }) + .unwrap(); system.run(); }); @@ -1720,13 +1934,16 @@ mod tests { let record = recording.get_record::(0); assert_eq!( record, - &RouteQueryMessage::data_indefinite_route_request(Some("nowhere.com".to_string()), 47) + &RouteQueryMessage::data_indefinite_route_request( + Host::new("nowhere.com", HTTP_PORT), + 47 + ) ); let recording = proxy_server_recording_arc.lock().unwrap(); assert_eq!(recording.len(), 0); TestLogHandler::new().exists_log_containing( - &format!("DEBUG: {test_name}: Found a new route for hostname: Some(\"nowhere.com\") - stream key: {stream_key} retries left: 3") + &format!("DEBUG: {test_name}: Found a new route for hostname: \"nowhere.com\" - stream key: {stream_key} retries left: 3") ); } @@ -1743,19 +1960,20 @@ mod tests { expected_services: ExpectedServices::RoundTrip( vec![make_exit_service_from_key(destination_key.clone())], vec![], - 1234, ), + host: Host::new("booga.com", HTTP_PORT), })); 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 = StreamKey::make_meaningless_stream_key(); let request_data = http_request.to_vec(); + let tunneled_data = make_server_com_client_hello(); let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr.clone(), - reception_port: Some(8443), - sequence_number: Some(0), + reception_port_opt: Some(8443), + sequence_number_opt: Some(0), last_data: false, is_clandestine: false, data: request_data.clone(), @@ -1763,27 +1981,27 @@ mod tests { let tunnelled_msg = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr, - reception_port: Some(8443), - sequence_number: Some(0), + reception_port_opt: Some(8443), + sequence_number_opt: Some(0), last_data: false, is_clandestine: false, - data: b"client hello".to_vec(), + data: tunneled_data.clone(), }; let expected_tdm = TransmitDataMsg { endpoint: Endpoint::Socket(socket_addr), last_data: false, - sequence_number: Some(0), + sequence_number_opt: 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 { - data: b"client hello".to_vec(), + data: tunneled_data, sequence_number: 0, last_data: false, }, - target_hostname: Some(String::from("realdomain.nu")), - target_port: 443, + target_hostname: String::from("realdomain.nu"), + target_port: TLS_PORT, protocol: ProxyProtocol::TLS, originator_public_key: alias_cryptde.public_key().clone(), }; @@ -1800,7 +2018,7 @@ mod tests { thread::spawn(move || { let stream_key_factory = StreamKeyFactoryMock::new() .make_parameters(&make_parameters_arc_thread) - .make_result(stream_key); + .make_result(stream_key.clone()); let system = System::new( "proxy_server_receives_connect_responds_with_ok_and_stores_stream_key_and_hostname", ); @@ -1822,6 +2040,13 @@ mod tests { subject_addr.try_send(msg_from_dispatcher).unwrap(); subject_addr.try_send(tunnelled_msg).unwrap(); + subject_addr + .try_send(AssertionsMessage { + assertions: Box::new(move |proxy_server: &mut ProxyServer| { + assert!(proxy_server.stream_info.contains_key(&stream_key)); + }), + }) + .unwrap(); system.run(); }); @@ -1844,8 +2069,8 @@ mod tests { assert_eq!( neighborhood_record, &RouteQueryMessage::data_indefinite_route_request( - Some("realdomain.nu".to_string()), - 12 + Host::new("realdomain.nu", TLS_PORT), + 68 ) ); } @@ -1868,15 +2093,18 @@ mod tests { subject .keys_and_addrs .insert(stream_key.clone(), socket_addr.clone()); - - subject.route_ids_to_return_routes.insert( - 1234, - AddReturnRouteMessage { - return_route_id: 1234, - expected_services: vec![ExpectedService::Nothing], - protocol: ProxyProtocol::TLS, - hostname_opt: None, - }, + subject.stream_info.insert( + stream_key.clone(), + StreamInfoBuilder::new() + .route(RouteQueryResponse { + route: make_meaningless_route(&CRYPTDE_PAIR), + expected_services: ExpectedServices::RoundTrip( + vec![], + vec![ExpectedService::Nothing], + ), + host: Host::new("booga.com", TLS_PORT), + }) + .build(), ); let subject_addr: Addr = subject.start(); let http_request = b"CONNECT https://realdomain.nu:443 HTTP/1.1\r\nHost: https://bunkjunk.wrong:443\r\n\r\n"; @@ -1884,10 +2112,10 @@ mod tests { let inbound_client_data = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr, - reception_port: Some(443), + reception_port_opt: Some(TLS_PORT), last_data: false, is_clandestine: false, - sequence_number: Some(0), + sequence_number_opt: Some(0), data: request_data, }; @@ -1904,7 +2132,7 @@ mod tests { ExpiredCoresPackage::new( SocketAddr::from_str("1.2.3.4:1234").unwrap(), Some(make_wallet("irrelevant")), - return_route_with_id(cryptde, 1234), + return_route(cryptde), client_response_payload.into(), 0, ); @@ -1921,9 +2149,10 @@ mod tests { system.run(); let dispatcher_recording = dispatcher_log_arc.lock().unwrap(); + let record = dispatcher_recording.get_record::(0); + assert_eq!(record.sequence_number_opt.unwrap(), 0); let record = dispatcher_recording.get_record::(1); - - assert_eq!(record.sequence_number.unwrap(), 1); + assert_eq!(record.sequence_number_opt.unwrap(), 1); } #[test] @@ -1936,7 +2165,7 @@ mod tests { let (dispatcher_mock, _dispatcher_awaiter, dispatcher_recording_arc) = make_recorder(); let neighborhood_mock = neighborhood_mock.route_query_response(Some( - zero_hop_route_response(&cryptde.public_key(), cryptde), + zero_hop_route_response(&cryptde.public_key(), cryptde, false), )); let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); @@ -1945,8 +2174,8 @@ mod tests { let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr.clone(), - reception_port: Some(8443), - sequence_number: Some(0), + reception_port_opt: Some(8443), + sequence_number_opt: Some(0), last_data: false, is_clandestine: false, data: request_data.clone(), @@ -1987,7 +2216,7 @@ mod tests { let expected_transmit_data_msg = TransmitDataMsg { endpoint: Endpoint::Socket(socket_addr), last_data: true, - sequence_number: Some(0), + sequence_number_opt: Some(0), data: b"HTTP/1.1 400 Bad Request\r\nContent-Length: 0\r\n\r\n".to_vec(), }; @@ -2007,7 +2236,7 @@ mod tests { let (dispatcher_mock, _dispatcher_awaiter, dispatcher_recording_arc) = make_recorder(); let neighborhood_mock = neighborhood_mock.route_query_response(Some( - zero_hop_route_response(&cryptde.public_key(), cryptde), + zero_hop_route_response(&cryptde.public_key(), cryptde, false), )); let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); @@ -2016,8 +2245,8 @@ mod tests { let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr.clone(), - reception_port: Some(8443), - sequence_number: Some(0), + reception_port_opt: Some(8443), + sequence_number_opt: Some(0), last_data: false, is_clandestine: false, data: request_data.clone(), @@ -2058,7 +2287,7 @@ mod tests { let expected_transmit_data_msg = TransmitDataMsg { endpoint: Endpoint::Socket(socket_addr), last_data: true, - sequence_number: Some(0), + sequence_number_opt: Some(0), data: b"HTTP/1.1 400 Bad Request\r\nContent-Length: 0\r\n\r\n".to_vec(), }; @@ -2082,8 +2311,8 @@ mod tests { let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr.clone(), - reception_port: Some(HTTP_PORT), - sequence_number: Some(0), + reception_port_opt: Some(HTTP_PORT), + sequence_number_opt: Some(0), last_data: true, is_clandestine: false, data: expected_data.clone(), @@ -2117,7 +2346,7 @@ mod tests { &TransmitDataMsg { endpoint: Endpoint::Socket(socket_addr), last_data: true, - sequence_number: Some(0), + sequence_number_opt: Some(0), data: server_impersonator.consuming_wallet_absent(), } ); @@ -2140,8 +2369,8 @@ mod tests { let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr.clone(), - reception_port: Some(TLS_PORT), - sequence_number: Some(0), + reception_port_opt: Some(TLS_PORT), + sequence_number_opt: Some(0), last_data: true, is_clandestine: false, data: expected_data.clone(), @@ -2175,7 +2404,7 @@ mod tests { &TransmitDataMsg { endpoint: Endpoint::Socket(socket_addr), last_data: true, - sequence_number: Some(0), + sequence_number_opt: Some(0), data: server_impersonator.consuming_wallet_absent(), } ); @@ -2192,7 +2421,8 @@ mod tests { let alias_cryptde = CRYPTDE_PAIR.alias.as_ref(); let expected_data = b"GET /index.html HTTP/1.1\r\nHost: nowhere.com\r\n\r\n".to_vec(); let expected_data_inner = expected_data.clone(); - let expected_route = zero_hop_route_response(main_cryptde.public_key(), main_cryptde); + let expected_route = + zero_hop_route_response(main_cryptde.public_key(), main_cryptde, false); let stream_key = StreamKey::make_meaningless_stream_key(); let (hopper, hopper_awaiter, hopper_log_arc) = make_recorder(); let neighborhood = Recorder::new().route_query_response(Some(expected_route.clone())); @@ -2203,8 +2433,8 @@ mod tests { let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr.clone(), - reception_port: Some(HTTP_PORT), - sequence_number: Some(0), + reception_port_opt: Some(HTTP_PORT), + sequence_number_opt: Some(0), last_data: true, is_clandestine: false, data: expected_data_inner, @@ -2213,7 +2443,12 @@ mod tests { let system = System::new("proxy_server_receives_http_request_with_no_consuming_wallet_in_zero_hop_mode_and_handles_normally"); let mut subject = ProxyServer::new(CRYPTDE_PAIR.clone(), false, None, false, false); subject.stream_key_factory = Box::new(stream_key_factory); - subject.keys_and_addrs.insert(stream_key, socket_addr); + subject + .keys_and_addrs + .insert(stream_key.clone(), socket_addr); + subject + .stream_info + .insert(stream_key, StreamInfoBuilder::new().build()); let subject_addr: Addr = subject.start(); let peer_actors = peer_actors_builder() .dispatcher(dispatcher) @@ -2235,7 +2470,7 @@ mod tests { target_component: Component::ProxyClient, return_component_opt: Some(Component::ProxyServer), payload_size: 47, - hostname_opt: Some("nowhere.com".to_string()), + host: Host::new("nowhere.com", HTTP_PORT), } ); let dispatcher_recording = dispatcher_log_arc.lock().unwrap(); @@ -2251,8 +2486,8 @@ mod tests { &ClientRequestPayload_0v1 { stream_key, sequenced_packet: SequencedPacket::new(expected_data, 0, true), - target_hostname: Some("nowhere.com".to_string()), - target_port: 80, + target_hostname: "nowhere.com".to_string(), + target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: alias_cryptde.public_key().clone(), } @@ -2271,28 +2506,35 @@ mod tests { let alias_cryptde = CRYPTDE_PAIR.alias.as_ref(); let expected_data = b"Fake TLS request".to_vec(); let expected_data_inner = expected_data.clone(); - let expected_route = zero_hop_route_response(main_cryptde.public_key(), main_cryptde); + let expected_route = zero_hop_route_response(main_cryptde.public_key(), main_cryptde, true); + let expected_route_inner = expected_route.clone(); let stream_key = StreamKey::make_meaningless_stream_key(); let (hopper, hopper_awaiter, hopper_log_arc) = make_recorder(); let neighborhood = Recorder::new().route_query_response(Some(expected_route.clone())); - let neighborhood_log_arc = neighborhood.get_recording(); let (dispatcher, _, dispatcher_log_arc) = make_recorder(); thread::spawn(move || { let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr.clone(), - reception_port: Some(TLS_PORT), - sequence_number: Some(0), + reception_port_opt: Some(TLS_PORT), + sequence_number_opt: Some(0), last_data: true, is_clandestine: false, data: expected_data_inner, }; - let stream_key_factory = StreamKeyFactoryMock::new(); // can't make any stream keys; shouldn't have to + let stream_key_factory = StreamKeyFactoryMock::new().make_result(stream_key.clone()); let system = System::new("proxy_server_receives_tls_request_with_no_consuming_wallet_in_zero_hop_mode_and_handles_normally"); let mut subject = ProxyServer::new(CRYPTDE_PAIR.clone(), false, None, false, false); subject.stream_key_factory = Box::new(stream_key_factory); subject.keys_and_addrs.insert(stream_key, socket_addr); + subject.stream_info.insert( + stream_key, + StreamInfoBuilder::new() + .route(expected_route_inner) + .protocol(ProxyProtocol::TLS) + .build(), + ); let subject_addr: Addr = subject.start(); let peer_actors = peer_actors_builder() .dispatcher(dispatcher) @@ -2306,17 +2548,6 @@ mod tests { system.run(); }); hopper_awaiter.await_message_count(1); - let neighborhood_recording = neighborhood_log_arc.lock().unwrap(); - assert_eq!( - neighborhood_recording.get_record::(0), - &RouteQueryMessage { - target_key_opt: None, - target_component: Component::ProxyClient, - return_component_opt: Some(Component::ProxyServer), - payload_size: 16, - hostname_opt: None, - } - ); let dispatcher_recording = dispatcher_log_arc.lock().unwrap(); assert!(dispatcher_recording.is_empty()); let hopper_recording = hopper_log_arc.lock().unwrap(); @@ -2330,8 +2561,8 @@ mod tests { &ClientRequestPayload_0v1 { stream_key, sequenced_packet: SequencedPacket::new(expected_data, 0, true), - target_hostname: None, - target_port: 443, + target_hostname: "booga.com".to_string(), + target_port: TLS_PORT, protocol: ProxyProtocol::TLS, originator_public_key: alias_cryptde.public_key().clone(), } @@ -2357,8 +2588,8 @@ mod tests { expected_services: ExpectedServices::RoundTrip( vec![make_exit_service_from_key(destination_key.clone())], vec![], - 1234, ), + host: Host::new("booga.com", HTTP_PORT), })); let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); let stream_key = StreamKey::make_meaningless_stream_key(); @@ -2366,8 +2597,8 @@ mod tests { let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr.clone(), - reception_port: Some(HTTP_PORT), - sequence_number: Some(0), + reception_port_opt: Some(HTTP_PORT), + sequence_number_opt: Some(0), last_data: true, is_clandestine: false, data: expected_data.clone(), @@ -2381,7 +2612,7 @@ mod tests { sequence_number: 0, last_data: true, }, - target_hostname: Some(String::from("nowhere.com")), + target_hostname: String::from("nowhere.com"), target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: alias_cryptde.public_key().clone(), @@ -2405,6 +2636,9 @@ mod tests { ); subject.stream_key_factory = Box::new(stream_key_factory); subject.keys_and_addrs.insert(stream_key, socket_addr); + subject + .stream_info + .insert(stream_key.clone(), StreamInfoBuilder::new().build()); let subject_addr: Addr = subject.start(); let peer_actors = peer_actors_builder() .hopper(hopper_mock) @@ -2456,7 +2690,6 @@ mod tests { ), main_cryptde, Some(consuming_wallet), - 1234, Some(TEST_DEFAULT_CHAIN.rec().contract), ) .unwrap(); @@ -2476,8 +2709,8 @@ mod tests { ExpectedService::Nothing, ExpectedService::Exit(PublicKey::new(&[3]), earning_wallet, rate_pack(102)), ], - 1234, ), + host: Host::new("booga.com", HTTP_PORT), })); let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); let stream_key = StreamKey::make_meaningless_stream_key(); @@ -2485,8 +2718,8 @@ mod tests { let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr.clone(), - reception_port: Some(HTTP_PORT), - sequence_number: Some(0), + reception_port_opt: Some(HTTP_PORT), + sequence_number_opt: Some(0), last_data: true, is_clandestine: false, data: expected_data.clone(), @@ -2499,7 +2732,7 @@ mod tests { sequence_number: 0, last_data: true, }, - target_hostname: Some(String::from("nowhere.com")), + target_hostname: String::from("nowhere.com"), target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: alias_cryptde.public_key().clone(), @@ -2542,7 +2775,10 @@ mod tests { let record = recording.get_record::(0); assert_eq!( record, - &RouteQueryMessage::data_indefinite_route_request(Some("nowhere.com".to_string()), 47) + &RouteQueryMessage::data_indefinite_route_request( + Host::new("nowhere.com", HTTP_PORT), + 47 + ) ); } @@ -2560,8 +2796,8 @@ mod tests { expected_services: ExpectedServices::RoundTrip( vec![expected_service.clone()], vec![expected_service], - 123, ), + host: Host::new("booga.com", HTTP_PORT), }); let (neighborhood_mock, _, _) = make_recorder(); let neighborhood_mock = @@ -2572,8 +2808,8 @@ mod tests { let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr.clone(), - reception_port: Some(HTTP_PORT), - sequence_number: Some(0), + reception_port_opt: Some(HTTP_PORT), + sequence_number_opt: Some(0), last_data: true, is_clandestine: false, data: expected_data.clone(), @@ -2632,8 +2868,8 @@ mod tests { let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr.clone(), - reception_port: Some(HTTP_PORT), - sequence_number: Some(0), + reception_port_opt: Some(HTTP_PORT), + sequence_number_opt: Some(0), last_data: true, is_clandestine: false, data: expected_data.clone(), @@ -2684,8 +2920,8 @@ mod tests { let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr.clone(), - reception_port: Some(HTTP_PORT), - sequence_number: Some(0), + reception_port_opt: Some(HTTP_PORT), + sequence_number_opt: Some(0), last_data: true, is_clandestine: false, data: expected_data.clone(), @@ -2719,13 +2955,14 @@ mod tests { let alias_cryptde = CRYPTDE_PAIR.alias.as_ref(); let http_request = b"GET /index.html HTTP/1.1\r\nHost: nowhere.com\r\n\r\n"; let destination_key = PublicKey::from(&b"our destination"[..]); + let route = Route { hops: vec![] }; let route_query_response = RouteQueryResponse { - route: Route { hops: vec![] }, + route: route.clone(), expected_services: ExpectedServices::RoundTrip( vec![make_exit_service_from_key(destination_key.clone())], vec![], - 1234, ), + host: Host::new("booga.com", HTTP_PORT), }; let (hopper_mock, hopper_awaiter, hopper_recording_arc) = make_recorder(); let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); @@ -2733,9 +2970,9 @@ mod tests { let expected_data = http_request.to_vec(); let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), - client_addr: socket_addr.clone(), - reception_port: Some(HTTP_PORT), - sequence_number: Some(0), + client_addr: socket_addr, + reception_port_opt: Some(HTTP_PORT), + sequence_number_opt: Some(0), last_data: true, is_clandestine: false, data: expected_data.clone(), @@ -2747,14 +2984,14 @@ mod tests { sequence_number: 0, last_data: true, }, - target_hostname: Some(String::from("nowhere.com")), + target_hostname: String::from("nowhere.com"), target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: alias_cryptde.public_key().clone(), }; let expected_pkg = IncipientCoresPackage::new( main_cryptde, - Route { hops: vec![] }, + route, expected_payload.into(), &destination_key, ) @@ -2772,8 +3009,12 @@ mod tests { ); subject.stream_key_factory = Box::new(stream_key_factory); subject - .stream_key_routes - .insert(stream_key, route_query_response); + .keys_and_addrs + .insert(stream_key.clone(), socket_addr); + subject.stream_info.insert( + stream_key, + StreamInfoBuilder::new().route(route_query_response).build(), + ); let subject_addr: Addr = subject.start(); let peer_actors = peer_actors_builder().hopper(hopper_mock).build(); subject_addr.try_send(BindMessage { peer_actors }).unwrap(); @@ -2793,57 +3034,89 @@ mod tests { fn proxy_server_sends_message_to_accountant_about_all_services_consumed_on_the_route_over() { let cryptde = CRYPTDE_PAIR.main.as_ref(); let now = SystemTime::now(); - let exit_earning_wallet = make_wallet("exit earning wallet"); - let route_1_earning_wallet = make_wallet("route 1 earning wallet"); - let route_2_earning_wallet = make_wallet("route 2 earning wallet"); + let routing_node_1_public_key = PublicKey::new(&[1]); + let routing_node_2_public_key = PublicKey::new(&[2]); + let exit_node_public_key = PublicKey::new(&[3]); + let key_bytes = b"__originating consuming wallet__"; + let keypair = Bip32EncryptionKeyProvider::from_raw_secret(key_bytes).unwrap(); + let originating_consuming_wallet = Wallet::from(keypair); + let routing_node_1_earning_wallet = make_wallet("route 1 earning wallet"); + let routing_node_2_earning_wallet = make_wallet("route 2 earning wallet"); + let exit_node_earning_wallet = make_wallet("exit earning wallet"); + 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 http_request = b"GET /index.html HTTP/1.1\r\nHost: nowhere.com\r\n\r\n"; let (accountant_mock, _, accountant_recording_arc) = make_recorder(); let (hopper_mock, _, hopper_recording_arc) = make_recorder(); 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 over_route_segment = RouteSegment::new( + vec![ + &cryptde.public_key(), + &routing_node_1_public_key, + &routing_node_2_public_key, + &exit_node_public_key, + ], + Component::ProxyClient, + ); + let back_route_segment = RouteSegment::new( + vec![ + &exit_node_public_key, + &routing_node_2_public_key, + &routing_node_1_public_key, + &cryptde.public_key(), + ], + Component::ProxyServer, + ); + let route = Route::round_trip( + over_route_segment, + back_route_segment, + cryptde, + Some(originating_consuming_wallet), + Some(TEST_DEFAULT_CHAIN.rec().contract), + ) + .unwrap(); let route_query_response = RouteQueryResponse { - route: make_meaningless_route(&CRYPTDE_PAIR), + route, expected_services: ExpectedServices::RoundTrip( vec![ ExpectedService::Nothing, ExpectedService::Routing( - PublicKey::new(&[1]), - route_1_earning_wallet.clone(), - routing_node_1_rate_pack, + routing_node_1_public_key.clone(), + routing_node_1_earning_wallet.clone(), + routing_node_1_rate_pack.clone(), ), ExpectedService::Routing( - PublicKey::new(&[2]), - route_2_earning_wallet.clone(), - routing_node_2_rate_pack, + routing_node_2_public_key.clone(), + routing_node_2_earning_wallet.clone(), + routing_node_2_rate_pack.clone(), ), ExpectedService::Exit( - PublicKey::new(&[3]), - exit_earning_wallet.clone(), - exit_node_rate_pack, + exit_node_public_key.clone(), + exit_node_earning_wallet.clone(), + exit_node_rate_pack.clone(), ), ], vec![ ExpectedService::Exit( - PublicKey::new(&[3]), - make_wallet("some wallet 1"), - rate_pack(104), + exit_node_public_key.clone(), + exit_node_earning_wallet.clone(), + exit_node_rate_pack, ), ExpectedService::Routing( - PublicKey::new(&[2]), - make_wallet("some wallet 2"), - rate_pack(105), + routing_node_2_public_key.clone(), + routing_node_2_earning_wallet.clone(), + routing_node_2_rate_pack, ), ExpectedService::Routing( - PublicKey::new(&[1]), - make_wallet("some wallet 3"), - rate_pack(106), + routing_node_1_public_key.clone(), + routing_node_1_earning_wallet.clone(), + routing_node_1_rate_pack, ), ExpectedService::Nothing, ], - 0, ), + host: Host::new("booga.com", HTTP_PORT), }; let source_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); let stream_key = StreamKey::make_meaningless_stream_key(); @@ -2859,7 +3132,7 @@ mod tests { let payload = ClientRequestPayload_0v1 { stream_key, sequenced_packet: SequencedPacket::new(expected_data, 0, false), - target_hostname: Some("nowhere.com".to_string()), + target_hostname: "nowhere.com".to_string(), target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: PublicKey::new(b"originator_public_key"), @@ -2878,17 +3151,35 @@ mod tests { retire_stream_key_sub_opt: None, }; - let result = ProxyServer::try_transmit_to_hopper( - args, - peer_actors.proxy_server.add_return_route, - route_query_response, - ); + let result = ProxyServer::try_transmit_to_hopper(args, route_query_response); System::current().stop(); system.run(); let recording = hopper_recording_arc.lock().unwrap(); - let record = recording.get_record::(0); + let mut record = recording.get_record::(0).clone(); let payload_enc_length = record.payload.len(); + let _ = record.route.shift(cryptde); + let _ = record.route.shift(&CryptDENull::from( + &routing_node_1_public_key, + TEST_DEFAULT_CHAIN, + )); + let _ = record.route.shift(&CryptDENull::from( + &routing_node_2_public_key, + TEST_DEFAULT_CHAIN, + )); + let _ = record.route.shift(&CryptDENull::from( + &exit_node_public_key, + TEST_DEFAULT_CHAIN, + )); + let _ = record.route.shift(&CryptDENull::from( + &routing_node_2_public_key, + TEST_DEFAULT_CHAIN, + )); + let _ = record.route.shift(&CryptDENull::from( + &routing_node_1_public_key, + TEST_DEFAULT_CHAIN, + )); + let _ = record.route.shift(cryptde); let recording = accountant_recording_arc.lock().unwrap(); let record = recording.get_record::(0); assert_eq!(recording.len(), 1); @@ -2897,7 +3188,7 @@ mod tests { &ReportServicesConsumedMessage { timestamp: now, exit: ExitServiceConsumed { - earning_wallet: exit_earning_wallet, + earning_wallet: exit_node_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 @@ -2905,12 +3196,12 @@ mod tests { routing_payload_size: payload_enc_length, routing: vec![ RoutingServiceConsumed { - earning_wallet: route_1_earning_wallet, + earning_wallet: routing_node_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, + earning_wallet: routing_node_2_earning_wallet, service_rate: routing_node_2_rate_pack.routing_service_rate, byte_rate: routing_node_2_rate_pack.routing_byte_rate, } @@ -2918,8 +3209,7 @@ mod tests { } ); let recording = proxy_server_recording_arc.lock().unwrap(); - let _ = recording.get_record::(0); // don't care about this, other than type - assert_eq!(recording.len(), 1); // No StreamShutdownMsg: that's the important thing + assert_eq!(recording.len(), 0); // No StreamShutdownMsg: that's the important thing assert_eq!(result, Ok(())); } @@ -2933,8 +3223,8 @@ mod tests { expected_services: ExpectedServices::RoundTrip( vec![ExpectedService::Nothing], vec![ExpectedService::Nothing], - 0, ), + host: Host::new("booga.com", HTTP_PORT), }; let source_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); let stream_key = StreamKey::make_meaningless_stream_key(); @@ -2947,7 +3237,7 @@ mod tests { let payload = ClientRequestPayload_0v1 { stream_key, sequenced_packet: SequencedPacket::new(expected_data, 0, false), - target_hostname: Some("nowhere.com".to_string()), + target_hostname: "nowhere.com".to_string(), target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: PublicKey::new(b"originator_public_key"), @@ -2966,26 +3256,12 @@ mod tests { retire_stream_key_sub_opt: Some(peer_actors.proxy_server.stream_shutdown_sub), }; - let result = ProxyServer::try_transmit_to_hopper( - args, - peer_actors.proxy_server.add_return_route, - route_query_response, - ); + let result = ProxyServer::try_transmit_to_hopper(args, route_query_response); System::current().stop(); system.run(); let recording = proxy_server_recording_arc.lock().unwrap(); - let record = recording.get_record::(0); - assert_eq!( - record, - &AddReturnRouteMessage { - return_route_id: 0, - expected_services: vec![ExpectedService::Nothing], - protocol: ProxyProtocol::HTTP, - hostname_opt: Some("nowhere.com".to_string()) - } - ); - let record = recording.get_record::(1); + let record = recording.get_record::(0); assert_eq!( record, &StreamShutdownMsg { @@ -3007,7 +3283,8 @@ mod tests { let http_request = b"GET /index.html HTTP/1.1\r\nHost: nowhere.com\r\n\r\n"; let (accountant_mock, accountant_awaiter, _) = make_recorder(); let (neighborhood_mock, _, _) = make_recorder(); - let mut route_query_response = zero_hop_route_response(&cryptde.public_key(), cryptde); + let mut route_query_response = + zero_hop_route_response(&cryptde.public_key(), cryptde, false); route_query_response.expected_services = ExpectedServices::RoundTrip( vec![ExpectedService::Exit( cryptde.public_key().clone(), @@ -3015,7 +3292,6 @@ mod tests { rate_pack(3), )], vec![], - 0, ); let neighborhood_mock = neighborhood_mock.route_query_response(Some(route_query_response.clone())); @@ -3025,8 +3301,8 @@ mod tests { let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr.clone(), - reception_port: Some(HTTP_PORT), - sequence_number: Some(0), + reception_port_opt: Some(HTTP_PORT), + sequence_number_opt: Some(0), last_data: true, is_clandestine: false, data: expected_data.clone(), @@ -3061,28 +3337,35 @@ mod tests { #[test] #[should_panic( - expected = "AddRouteResultMessage Handler: stream key: AAAAAAAAAAAAAAAAAAAAAAAAAAA not found within dns_failure_retries" + expected = "AddRouteResultMessage Handler: dns_failure_retry_opt is None for stream key" )] - fn route_result_message_handler_panics_when_dns_retries_hashmap_doesnt_contain_a_stream_key() { - let system = System::new("route_result_message_handler_panics_when_dns_retries_hashmap_doesnt_contain_a_stream_key"); - let subject = ProxyServer::new( + fn route_result_message_handler_panics_when_no_dns_retries_exist() { + init_test_logging(); + let system = System::new("route_result_message_handler_panics_when_no_dns_retries_exist"); + let mut subject = ProxyServer::new( CRYPTDE_PAIR.clone(), true, Some(STANDARD_CONSUMING_WALLET_BALANCE), false, false, ); + let stream_key = StreamKey::make_meaningless_stream_key(); + subject.stream_info.insert( + stream_key.clone(), + StreamInfoBuilder::new().build(), // no DNS retries + ); let subject_addr: Addr = subject.start(); let peer_actors = peer_actors_builder().build(); subject_addr.try_send(BindMessage { peer_actors }).unwrap(); subject_addr .try_send(AddRouteResultMessage { - stream_key: StreamKey::make_meaningless_stream_key(), + stream_key, result: Err("Some Error".to_string()), }) .unwrap(); + System::current().stop(); system.run(); } @@ -3144,8 +3427,8 @@ mod tests { let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr.clone(), - reception_port: Some(HTTP_PORT), - sequence_number: Some(0), + reception_port_opt: Some(HTTP_PORT), + sequence_number_opt: Some(0), last_data: true, data: expected_data.clone(), is_clandestine: false, @@ -3181,7 +3464,7 @@ mod tests { let expected_msg = TransmitDataMsg { endpoint: Endpoint::Socket(SocketAddr::from_str("1.2.3.4:5678").unwrap()), last_data: true, - sequence_number: Some(0), + sequence_number_opt: Some(0), data: ServerImpersonatorHttp {}.route_query_failure_response("nowhere.com"), }; assert_eq!(record, &expected_msg); @@ -3189,10 +3472,13 @@ mod tests { let record = recording.get_record::(0); assert_eq!( record, - &RouteQueryMessage::data_indefinite_route_request(Some("nowhere.com".to_string()), 47) + &RouteQueryMessage::data_indefinite_route_request( + Host::new("nowhere.com", HTTP_PORT), + 47 + ) ); TestLogHandler::new().exists_log_containing(&format!( - "WARN: {test_name}: No route found for hostname: Some(\"nowhere.com\") - stream key {stream_key} - retries left: 3 - AddRouteResultMessage Error: Failed to find route to nowhere.com" + "WARN: {test_name}: No route found for hostname: \"nowhere.com\" - stream key {stream_key} - retries left: 3 - AddRouteResultMessage Error: Failed to find route to nowhere.com" )); } @@ -3223,6 +3509,7 @@ mod tests { rate_pack(103), ), ]), + host: Host::new("booga.com", HTTP_PORT), }; let payload = ClientRequestPayload_0v1 { stream_key: StreamKey::make_meaningless_stream_key(), @@ -3231,7 +3518,7 @@ mod tests { sequence_number: 0, last_data: false, }, - target_hostname: None, + target_hostname: "booga.com".to_string(), target_port: 0, protocol: ProxyProtocol::TLS, originator_public_key: cryptde.public_key().clone(), @@ -3251,11 +3538,7 @@ mod tests { retire_stream_key_sub_opt: None, }; - let _result = ProxyServer::try_transmit_to_hopper( - args, - peer_actors.proxy_server.add_return_route, - route_result, - ); + let _result = ProxyServer::try_transmit_to_hopper(args, route_result); } #[test] @@ -3269,25 +3552,20 @@ mod tests { false, 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, - hostname_opt: None, - }; + let 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), + ), + ]; - subject.report_response_services_consumed(&add_return_route_message, 1234, 3456); + subject.report_response_services_consumed(&expected_services, 1234, 3456); } #[test] @@ -3305,11 +3583,11 @@ mod tests { RouteSegment::new(vec![public_key, public_key], Component::ProxyServer), cryptde, None, - 1234, None, ) .unwrap(), - expected_services: ExpectedServices::RoundTrip(vec![], vec![], 1234), + expected_services: ExpectedServices::RoundTrip(vec![], vec![]), + host: Host::new("booga.com", HTTP_PORT), }; let neighborhood_mock = neighborhood_mock.route_query_response(Some(route_query_response)); let dispatcher = Recorder::new(); @@ -3320,8 +3598,8 @@ mod tests { let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr.clone(), - reception_port: Some(HTTP_PORT), - sequence_number: Some(0), + reception_port_opt: Some(HTTP_PORT), + sequence_number_opt: Some(0), last_data: true, data: expected_data.clone(), is_clandestine: false, @@ -3358,7 +3636,7 @@ mod tests { let expected_msg = TransmitDataMsg { endpoint: Endpoint::Socket(SocketAddr::from_str("1.2.3.4:5678").unwrap()), last_data: true, - sequence_number: Some(0), + sequence_number_opt: Some(0), data: ServerImpersonatorHttp {}.route_query_failure_response("nowhere.com"), }; assert_eq!(record, &expected_msg); @@ -3366,35 +3644,19 @@ mod tests { let record = recording.get_record::(0); assert_eq!( record, - &RouteQueryMessage::data_indefinite_route_request(Some("nowhere.com".to_string()), 47) + &RouteQueryMessage::data_indefinite_route_request( + Host::new("nowhere.com", HTTP_PORT), + 47 + ) ); TestLogHandler::new().exists_log_containing(&format!( - "WARN: {test_name}: No route found for hostname: Some(\"nowhere.com\") - stream key {stream_key} - retries left: 3 - AddRouteResultMessage Error: Failed to find route to nowhere.com" + "WARN: {test_name}: No route found for hostname: \"nowhere.com\" - stream key {stream_key} - retries left: 3 - AddRouteResultMessage Error: Failed to find route to nowhere.com" )); } #[test] fn proxy_server_receives_tls_client_hello_from_dispatcher_then_sends_cores_package_to_hopper() { - let tls_request = &[ - 0x16, // content_type: Handshake - 0x00, 0x00, 0x00, 0x00, // version, length: don't care - 0x01, // handshake_type: ClientHello - 0x00, 0x00, 0x00, 0x00, 0x00, // length, version: don't care - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, // random: don't care - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, // random: don't care - 0x00, // session_id_length - 0x00, 0x00, // cipher_suites_length - 0x00, // compression_methods_length - 0x00, 0x13, // extensions_length - 0x00, 0x00, // extension_type: server_name - 0x00, 0x0F, // extension_length - 0x00, 0x0D, // server_name_list_length - 0x00, // server_name_type - 0x00, 0x0A, // server_name_length - b's', b'e', b'r', b'v', b'e', b'r', b'.', b'c', b'o', b'm', // server_name - ]; + let tls_request = make_server_com_client_hello(); let main_cryptde = CRYPTDE_PAIR.main.as_ref(); let alias_cryptde = CRYPTDE_PAIR.alias.as_ref(); let hopper_mock = Recorder::new(); @@ -3406,22 +3668,22 @@ mod tests { expected_services: ExpectedServices::RoundTrip( vec![make_exit_service_from_key(destination_key.clone())], vec![], - 1234, ), + host: Host::new("booga.com", TLS_PORT), })); let stream_key = StreamKey::make_meaningless_stream_key(); let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); - let expected_data = tls_request.to_vec(); + let expected_data = tls_request.clone(); let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr.clone(), - reception_port: Some(TLS_PORT), - sequence_number: Some(0), + reception_port_opt: Some(TLS_PORT), + sequence_number_opt: Some(0), last_data: false, is_clandestine: false, data: expected_data.clone(), }; - let expected_tls_request = PlainData::new(tls_request); + let expected_tls_request = PlainData::new(tls_request.as_slice()); let route = Route { hops: vec![] }; let expected_payload = ClientRequestPayload_0v1 { stream_key: stream_key.clone(), @@ -3430,7 +3692,7 @@ mod tests { sequence_number: 0, last_data: false, }, - target_hostname: Some(String::from("server.com")), + target_hostname: String::from("server.com"), target_port: TLS_PORT, protocol: ProxyProtocol::TLS, originator_public_key: alias_cryptde.public_key().clone(), @@ -3491,8 +3753,8 @@ mod tests { expected_services: ExpectedServices::RoundTrip( vec![make_exit_service_from_key(destination_key.clone())], vec![], - 1234, ), + host: Host::new("booga.com", TLS_PORT), })); let stream_key = StreamKey::make_meaningless_stream_key(); let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); @@ -3500,8 +3762,8 @@ mod tests { let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr.clone(), - reception_port: Some(TLS_PORT), - sequence_number: Some(0), + reception_port_opt: Some(TLS_PORT), + sequence_number_opt: Some(0), last_data: false, is_clandestine: false, data: expected_data.clone(), @@ -3515,7 +3777,7 @@ mod tests { sequence_number: 0, last_data: false, }, - target_hostname: None, + target_hostname: "booga.com".to_string(), target_port: TLS_PORT, protocol: ProxyProtocol::TLS, originator_public_key: alias_cryptde.public_key().clone(), @@ -3537,6 +3799,22 @@ mod tests { ); subject.stream_key_factory = Box::new(StreamKeyFactoryMock::new().make_result(stream_key.clone())); + subject + .keys_and_addrs + .insert(stream_key.clone(), socket_addr); + subject.stream_info.insert( + stream_key.clone(), + StreamInfoBuilder::new() + .route(RouteQueryResponse { + route: Route { hops: vec![] }, + expected_services: ExpectedServices::RoundTrip( + vec![make_exit_service_from_key(destination_key.clone())], + vec![], + ), + host: Host::new("booga.com", TLS_PORT), + }) + .build(), + ); 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 peer_actors = peer_actors_builder() @@ -3549,7 +3827,6 @@ mod tests { system.run(); }); - hopper_awaiter.await_message_count(1); let recording = hopper_log_arc.lock().unwrap(); let record = recording.get_record::(0); @@ -3575,8 +3852,8 @@ mod tests { expected_services: ExpectedServices::RoundTrip( vec![make_exit_service_from_key(destination_key.clone())], vec![], - 1234, ), + host: Host::new("booga.com", TLS_PORT), })); let client_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); let stream_key = StreamKey::make_meaningful_stream_key(test_name); @@ -3584,8 +3861,8 @@ mod tests { let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr, - reception_port: Some(TLS_PORT), - sequence_number: Some(0), + reception_port_opt: Some(TLS_PORT), + sequence_number_opt: Some(0), last_data: true, is_clandestine: false, data: expected_data.clone(), @@ -3599,7 +3876,7 @@ mod tests { sequence_number: 0, last_data: true, }, - target_hostname: None, + target_hostname: "booga.com".to_string(), target_port: TLS_PORT, protocol: ProxyProtocol::TLS, originator_public_key: alias_cryptde.public_key().clone(), @@ -3620,6 +3897,20 @@ mod tests { false, ); subject.keys_and_addrs.insert(stream_key, client_addr); + subject.stream_info.insert( + stream_key, + StreamInfoBuilder::new() + .route(RouteQueryResponse { + route: Route { hops: vec![] }, + expected_services: ExpectedServices::RoundTrip( + vec![make_exit_service_from_key(destination_key.clone())], + vec![], + ), + host: Host::new("booga.com", TLS_PORT), + }) + .protocol(ProxyProtocol::TLS) + .build(), + ); let system = System::new(test_name); let subject_addr: Addr = subject.start(); let peer_actors = peer_actors_builder() @@ -3673,8 +3964,8 @@ mod tests { let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr.clone(), - reception_port: Some(TLS_PORT), - sequence_number: Some(0), + reception_port_opt: Some(TLS_PORT), + sequence_number_opt: Some(0), last_data: true, data: tls_request, is_clandestine: false, @@ -3708,7 +3999,7 @@ mod tests { let expected_msg = TransmitDataMsg { endpoint: Endpoint::Socket(SocketAddr::from_str("1.2.3.4:5678").unwrap()), last_data: true, - sequence_number: Some(0), + sequence_number_opt: Some(0), data: ServerImpersonatorTls {}.route_query_failure_response("ignored"), }; assert_eq!(record, &expected_msg); @@ -3734,17 +4025,22 @@ mod tests { subject .keys_and_addrs .insert(stream_key.clone(), socket_addr.clone()); - subject.route_ids_to_return_routes.insert( - 1234, - AddReturnRouteMessage { - return_route_id: 1234, - expected_services: vec![ExpectedService::Nothing], - protocol: ProxyProtocol::TLS, - hostname_opt: None, - }, + subject.stream_info.insert( + stream_key.clone(), + StreamInfoBuilder::new() + .route(RouteQueryResponse { + route: Route { hops: vec![] }, + expected_services: ExpectedServices::RoundTrip( + vec![], + vec![ExpectedService::Nothing], + ), + host: Host::new("booga.com", TLS_PORT), + }) + .protocol(ProxyProtocol::TLS) + .build(), ); let subject_addr: Addr = subject.start(); - let remaining_route = return_route_with_id(cryptde, 1234); + let remaining_route = return_route(cryptde); let client_response_payload = ClientResponsePayload_0v1 { stream_key: stream_key.clone(), sequenced_packet: SequencedPacket { @@ -3780,7 +4076,7 @@ mod tests { stream_key )); tlh.exists_log_containing(&format!( - "WARN: {test_name}: Discarding 16-byte packet 12345678 from an unrecognized stream key: {:?}", + "ERROR: {test_name}: Can't pay for return services consumed: received response with unrecognized stream key {:?}. Ignoring", stream_key )); } @@ -3819,24 +4115,17 @@ mod tests { subject .keys_and_addrs .insert(stream_key.clone(), socket_addr.clone()); - subject.stream_key_routes.insert( + subject.stream_info.insert( stream_key.clone(), - RouteQueryResponse { - route: Route { hops: vec![] }, - expected_services: ExpectedServices::RoundTrip(vec![], vec![], 1234), - }, - ); - subject - .tunneled_hosts - .insert(stream_key.clone(), "hostname".to_string()); - subject.route_ids_to_return_routes.insert( - 1234, - AddReturnRouteMessage { - return_route_id: 1234, - expected_services: vec![], - protocol: ProxyProtocol::HTTP, - hostname_opt: None, - }, + StreamInfoBuilder::new() + .route(RouteQueryResponse { + route: Route { hops: vec![] }, + expected_services: ExpectedServices::RoundTrip(vec![], vec![]), + host: Host::new("booga.com", HTTP_PORT), + }) + .protocol(ProxyProtocol::HTTP) + .tunneled_host("hostname") + .build(), ); let client_response_payload = ClientResponsePayload_0v1 { stream_key: stream_key.clone(), @@ -3849,7 +4138,7 @@ mod tests { ExpiredCoresPackage::new( SocketAddr::from_str("1.2.3.4:1234").unwrap(), Some(make_wallet("irrelevant")), - return_route_with_id(cryptde, 1234), + return_route(cryptde), client_response_payload.into(), 0, ); @@ -3857,8 +4146,7 @@ mod tests { subject.handle_client_response_payload(expired_cores_package); assert!(subject.keys_and_addrs.is_empty()); - assert!(subject.stream_key_routes.is_empty()); - assert!(subject.tunneled_hosts.is_empty()); + assert!(subject.stream_info.get(&stream_key).is_none()); } #[test] @@ -3926,24 +4214,17 @@ mod tests { subject .keys_and_addrs .insert(stream_key.clone(), msg.peer_addr.clone()); - subject.stream_key_routes.insert( + subject.stream_info.insert( stream_key.clone(), - RouteQueryResponse { - route: Route { hops: vec![] }, - expected_services: ExpectedServices::RoundTrip(vec![], vec![], 1234), - }, - ); - subject - .tunneled_hosts - .insert(stream_key.clone(), "hostname".to_string()); - subject.route_ids_to_return_routes.insert( - 1234, - AddReturnRouteMessage { - return_route_id: 1234, - expected_services: vec![], - protocol: ProxyProtocol::HTTP, - hostname_opt: None, - }, + StreamInfoBuilder::new() + .route(RouteQueryResponse { + route: Route { hops: vec![] }, + expected_services: ExpectedServices::RoundTrip(vec![], vec![]), + host: Host::new("booga.com", HTTP_PORT), + }) + .protocol(ProxyProtocol::HTTP) + .tunneled_host("hostname") + .build(), ); let proxy_server_addr = subject.start(); let schedule_stream_key_purge_sub = proxy_server_addr.clone().recipient(); @@ -3961,18 +4242,14 @@ mod tests { .unwrap(); let pre_purge_assertions = AssertionsMessage { assertions: Box::new(move |proxy_server: &mut ProxyServer| { - let purge_timestamp = proxy_server - .stream_key_ttl - .get(&stream_key) - .unwrap() - .clone(); + let stream_info = proxy_server.stream_info.get(&stream_key).unwrap(); + let purge_timestamp = stream_info.time_to_live_opt.unwrap(); assert!( time_before_sending_package <= purge_timestamp && purge_timestamp <= time_after_sending_package ); + assert!(!proxy_server.stream_info.get(&stream_key).is_none()); assert!(!proxy_server.keys_and_addrs.is_empty()); - assert!(!proxy_server.stream_key_routes.is_empty()); - assert!(!proxy_server.tunneled_hosts.is_empty()); TestLogHandler::new().exists_log_containing(&format!( "DEBUG: {test_name}: Client closed stream referenced by stream key {:?}, \ which was tunneling to the host \"hostname\". \ @@ -3990,9 +4267,7 @@ mod tests { let post_purge_assertions = AssertionsMessage { assertions: Box::new(move |proxy_server: &mut ProxyServer| { assert!(proxy_server.keys_and_addrs.is_empty()); - assert!(proxy_server.stream_key_routes.is_empty()); - assert!(proxy_server.tunneled_hosts.is_empty()); - assert!(proxy_server.stream_key_ttl.is_empty()); + assert!(proxy_server.stream_info.get(&stream_key).is_none()); TestLogHandler::new().exists_log_containing(&format!( "DEBUG: {test_name}: Retiring stream key {:?}", stream_key @@ -4028,16 +4303,6 @@ mod tests { subject .keys_and_addrs .insert(stream_key.clone(), socket_addr.clone()); - subject.stream_key_routes.insert( - stream_key.clone(), - RouteQueryResponse { - route: Route { hops: vec![] }, - expected_services: ExpectedServices::RoundTrip(vec![], vec![], 1234), - }, - ); - subject - .tunneled_hosts - .insert(stream_key.clone(), "hostname".to_string()); let exit_key = PublicKey::new(&b"blah"[..]); let exit_wallet = make_wallet("abc"); let exit_rates = RatePack { @@ -4046,22 +4311,26 @@ mod tests { exit_byte_rate: 100, exit_service_rate: 60000, }; - subject.route_ids_to_return_routes.insert( - 1234, - AddReturnRouteMessage { - return_route_id: 1234, - expected_services: vec![ExpectedService::Exit( - exit_key, - exit_wallet.clone(), - exit_rates.clone(), - )], - protocol: ProxyProtocol::HTTP, - hostname_opt: None, - }, + subject.stream_info.insert( + stream_key.clone(), + StreamInfoBuilder::new() + .route(RouteQueryResponse { + route: Route { hops: vec![] }, + expected_services: ExpectedServices::RoundTrip( + vec![], + vec![ExpectedService::Exit( + exit_key, + exit_wallet.clone(), + exit_rates.clone(), + )], + ), + host: Host::new("booga.com", HTTP_PORT), + }) + .tunneled_host("hostname") + .protocol(ProxyProtocol::HTTP) + .time_to_live(SystemTime::now()) + .build(), ); - subject - .stream_key_ttl - .insert(stream_key.clone(), SystemTime::now()); let (accountant, _, accountant_recording_arc) = make_recorder(); let (dispatcher, _, dispatcher_recording_arc) = make_recorder(); let proxy_server_addr = subject.start(); @@ -4079,7 +4348,7 @@ mod tests { ExpiredCoresPackage::new( SocketAddr::from_str("1.2.3.4:1234").unwrap(), Some(make_wallet("irrelevant")), - return_route_with_id(cryptde, 1234), + return_route(cryptde), client_response_payload.into(), 5432, ); @@ -4137,63 +4406,35 @@ mod tests { 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 { - return_route_id: 1234, - expected_services: vec![ - ExpectedService::Exit( - irrelevant_public_key.clone(), - incoming_route_d_wallet.clone(), - rate_pack_d, - ), - ExpectedService::Routing( - irrelevant_public_key.clone(), - incoming_route_e_wallet.clone(), - rate_pack_e, - ), - ExpectedService::Routing( - irrelevant_public_key.clone(), - incoming_route_f_wallet.clone(), - rate_pack_f, + subject.stream_info.insert( + stream_key.clone(), + StreamInfoBuilder::new() + .route(RouteQueryResponse { + route: Route { hops: vec![] }, + expected_services: ExpectedServices::RoundTrip( + vec![], + vec![ + ExpectedService::Exit( + irrelevant_public_key.clone(), + incoming_route_d_wallet.clone(), + rate_pack_d, + ), + ExpectedService::Routing( + irrelevant_public_key.clone(), + incoming_route_e_wallet.clone(), + rate_pack_e, + ), + ExpectedService::Routing( + irrelevant_public_key.clone(), + incoming_route_f_wallet.clone(), + rate_pack_f, + ), + ExpectedService::Nothing, + ], ), - ExpectedService::Nothing, - ], - protocol: ProxyProtocol::TLS, - hostname_opt: None, - }, - ); - 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 { - return_route_id: 1235, - expected_services: vec![ - ExpectedService::Exit( - irrelevant_public_key.clone(), - incoming_route_g_wallet.clone(), - rate_pack_g, - ), - ExpectedService::Routing( - irrelevant_public_key.clone(), - incoming_route_h_wallet.clone(), - rate_pack_h, - ), - ExpectedService::Routing( - irrelevant_public_key.clone(), - incoming_route_i_wallet.clone(), - rate_pack_i, - ), - ExpectedService::Nothing, - ], - protocol: ProxyProtocol::TLS, - hostname_opt: None, - }, + host: Host::new("booga.com", HTTP_PORT), + }) + .build(), ); let subject_addr: Addr = subject.start(); let first_client_response_payload = ClientResponsePayload_0v1 { @@ -4209,7 +4450,7 @@ mod tests { ExpiredCoresPackage::new( SocketAddr::from_str("1.2.3.4:1234").unwrap(), Some(make_wallet("irrelevant")), - return_route_with_id(cryptde, 1234), + return_route(cryptde), first_client_response_payload.into(), 0, ); @@ -4227,7 +4468,7 @@ mod tests { ExpiredCoresPackage::new( SocketAddr::from_str("1.2.3.5:1235").unwrap(), Some(make_wallet("irrelevant")), - return_route_with_id(cryptde, 1235), + return_route(cryptde), second_client_response_payload.into(), 0, ); @@ -4265,7 +4506,7 @@ mod tests { &ReportServicesConsumedMessage { timestamp: first_report_timestamp, exit: ExitServiceConsumed { - earning_wallet: incoming_route_d_wallet, + earning_wallet: incoming_route_d_wallet.clone(), payload_size: first_exit_size, service_rate: rate_pack_d.exit_service_rate, byte_rate: rate_pack_d.exit_byte_rate @@ -4273,12 +4514,12 @@ mod tests { routing_payload_size: routing_size, routing: vec![ RoutingServiceConsumed { - earning_wallet: incoming_route_e_wallet, + earning_wallet: incoming_route_e_wallet.clone(), service_rate: rate_pack_e.routing_service_rate, byte_rate: rate_pack_e.routing_byte_rate }, RoutingServiceConsumed { - earning_wallet: incoming_route_f_wallet, + earning_wallet: incoming_route_f_wallet.clone(), service_rate: rate_pack_f.routing_service_rate, byte_rate: rate_pack_f.routing_byte_rate } @@ -4294,22 +4535,22 @@ mod tests { &ReportServicesConsumedMessage { timestamp: second_report_timestamp, exit: ExitServiceConsumed { - earning_wallet: incoming_route_g_wallet, + earning_wallet: incoming_route_d_wallet, payload_size: second_exit_size, - service_rate: rate_pack_g.exit_service_rate, - byte_rate: rate_pack_g.exit_byte_rate + 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_h_wallet, - service_rate: rate_pack_h.routing_service_rate, - byte_rate: rate_pack_h.routing_byte_rate + 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_i_wallet, - service_rate: rate_pack_i.routing_service_rate, - byte_rate: rate_pack_i.routing_byte_rate + earning_wallet: incoming_route_f_wallet, + service_rate: rate_pack_f.routing_service_rate, + byte_rate: rate_pack_f.routing_byte_rate } ] } @@ -4339,48 +4580,48 @@ mod tests { .keys_and_addrs .insert(stream_key.clone(), socket_addr.clone()); subject.logger = Logger::new(test_name); - let mut dns_failure_retries_hash_map = HashMap::new(); let mut dns_fail_client_payload = make_request_payload(111, cryptde); dns_fail_client_payload.stream_key = stream_key; - dns_failure_retries_hash_map.insert( - stream_key, - DNSFailureRetry { - unsuccessful_request: dns_fail_client_payload, - retries_left: 3, - }, - ); - subject.dns_failure_retries = dns_failure_retries_hash_map; 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 { - return_route_id: 1234, - expected_services: vec![ - ExpectedService::Exit( - irrelevant_public_key.clone(), - incoming_route_d_wallet.clone(), - rate_pack_d, - ), - ExpectedService::Routing( - irrelevant_public_key.clone(), - incoming_route_e_wallet.clone(), - rate_pack_e, - ), - ExpectedService::Routing( - irrelevant_public_key.clone(), - incoming_route_f_wallet.clone(), - rate_pack_f, + subject.stream_info.insert( + stream_key_clone.clone(), + StreamInfoBuilder::new() + .dns_failure_retry(DNSFailureRetry { + unsuccessful_request: dns_fail_client_payload, + retries_left: 3, + }) + .route(RouteQueryResponse { + route: Route { hops: vec![] }, + expected_services: ExpectedServices::RoundTrip( + vec![], + vec![ + ExpectedService::Exit( + irrelevant_public_key.clone(), + incoming_route_d_wallet.clone(), + rate_pack_d, + ), + ExpectedService::Routing( + irrelevant_public_key.clone(), + incoming_route_e_wallet.clone(), + rate_pack_e, + ), + ExpectedService::Routing( + irrelevant_public_key.clone(), + incoming_route_f_wallet.clone(), + rate_pack_f, + ), + ExpectedService::Nothing, + ], ), - ExpectedService::Nothing, - ], - protocol: ProxyProtocol::TLS, - hostname_opt: None, - }, + host: Host::new("booga.com", TLS_PORT), + }) + .protocol(ProxyProtocol::TLS) + .build(), ); let subject_addr: Addr = subject.start(); let first_client_response_payload = ClientResponsePayload_0v1 { @@ -4395,7 +4636,7 @@ mod tests { ExpiredCoresPackage::new( SocketAddr::from_str("1.2.3.4:1234").unwrap(), Some(make_wallet("irrelevant")), - return_route_with_id(cryptde, 1234), + return_route(cryptde), first_client_response_payload.into(), 0, ); @@ -4407,14 +4648,16 @@ mod tests { subject_addr .try_send(AssertionsMessage { assertions: Box::new(move |proxy_server: &mut ProxyServer| { - let retry_opt = proxy_server.dns_failure_retries.get(&stream_key); - assert_eq!(retry_opt, None); + let retry_opt = &proxy_server + .stream_info(&stream_key_clone) + .unwrap() + .dns_failure_retry_opt; + assert!(retry_opt.is_none()); }), }) .unwrap(); System::current().stop(); system.run(); - TestLogHandler::new().exists_log_containing(&format!("DEBUG: {test_name}: Successful attempt of DNS resolution, removing DNS retry entry for stream key: {stream_key_clone}")); } #[test] @@ -4438,25 +4681,30 @@ mod tests { 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 { - return_route_id: 1234, - expected_services: vec![ - ExpectedService::Exit( - irrelevant_public_key.clone(), - incoming_route_d_wallet.clone(), - rate_pack_d, - ), - ExpectedService::Routing( - irrelevant_public_key.clone(), - incoming_route_e_wallet.clone(), - rate_pack_e, + subject.stream_info.insert( + stream_key.clone(), + StreamInfoBuilder::new() + .route(RouteQueryResponse { + route: Route { hops: vec![] }, + expected_services: ExpectedServices::RoundTrip( + vec![], + vec![ + ExpectedService::Exit( + irrelevant_public_key.clone(), + incoming_route_d_wallet.clone(), + rate_pack_d, + ), + ExpectedService::Routing( + irrelevant_public_key.clone(), + incoming_route_e_wallet.clone(), + rate_pack_e, + ), + ], ), - ], - protocol: ProxyProtocol::TLS, - hostname_opt: None, - }, + host: Host::new("booga.com", TLS_PORT), + }) + .protocol(ProxyProtocol::TLS) + .build(), ); let subject_addr: Addr = subject.start(); let client_response_payload = ClientResponsePayload_0v1 { @@ -4472,7 +4720,7 @@ mod tests { ExpiredCoresPackage::new( SocketAddr::from_str("1.2.3.4:1234").unwrap(), Some(make_wallet("irrelevant")), - return_route_with_id(cryptde, 1234), + return_route(cryptde), client_response_payload.into(), 0, ); @@ -4534,46 +4782,47 @@ mod tests { let stream_key = StreamKey::make_meaningless_stream_key(); let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); - let mut dns_failure_retries_hash_map = HashMap::new(); let client_payload = make_request_payload(111, cryptde); - dns_failure_retries_hash_map.insert( - stream_key, - DNSFailureRetry { - unsuccessful_request: client_payload, - retries_left: 0, - }, - ); - subject.dns_failure_retries = dns_failure_retries_hash_map; + let exit_public_key = PublicKey::from(&b"exit_key"[..]); + let exit_wallet = make_wallet("exit wallet"); 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.stream_info.insert( + stream_key.clone(), + StreamInfoBuilder::new() + .dns_failure_retry(DNSFailureRetry { + unsuccessful_request: client_payload, + retries_left: 0, + }) + .route(RouteQueryResponse { + route: Route { hops: vec![] }, + expected_services: ExpectedServices::RoundTrip( + vec![], + vec![ExpectedService::Exit( + exit_public_key.clone(), + exit_wallet, + rate_pack(10), + )], + ), + host: Host::new("server.com", HTTP_PORT), + }) + .protocol(ProxyProtocol::HTTP) + .build(), + ); 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(), Some(make_wallet("irrelevant")), - return_route_with_id(cryptde, 1234), + return_route(cryptde), dns_resolve_failure.into(), 0, ); let peer_actors = peer_actors_builder().dispatcher(dispatcher_mock).build(); subject_addr.try_send(BindMessage { peer_actors }).unwrap(); - subject_addr - .try_send(AddReturnRouteMessage { - return_route_id: 1234, - expected_services: vec![ExpectedService::Exit( - exit_public_key.clone(), - exit_wallet, - rate_pack(10), - )], - protocol: ProxyProtocol::HTTP, - hostname_opt: Some("server.com".to_string()), - }) - .unwrap(); subject_addr.try_send(expired_cores_package).unwrap(); System::current().stop(); @@ -4585,9 +4834,9 @@ mod tests { TransmitDataMsg { endpoint: Endpoint::Socket(socket_addr), last_data: true, - sequence_number: Some(0), + sequence_number_opt: Some(0), data: ServerImpersonatorHttp {} - .dns_resolution_failure_response(Some("server.com".to_string()),), + .dns_resolution_failure_response("server.com".to_string()), }, *record ); @@ -4608,16 +4857,7 @@ mod tests { let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); let stream_key = StreamKey::make_meaningless_stream_key(); let irrelevant_public_key = PublicKey::from(&b"irrelevant"[..]); - let mut dns_failure_retries_hash_map = HashMap::new(); let client_payload = make_request_payload(111, cryptde); - dns_failure_retries_hash_map.insert( - stream_key, - DNSFailureRetry { - unsuccessful_request: client_payload, - retries_left: 0, - }, - ); - subject.dns_failure_retries = dns_failure_retries_hash_map; subject .keys_and_addrs .insert(stream_key.clone(), socket_addr.clone()); @@ -4627,31 +4867,40 @@ mod tests { 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 { - return_route_id: 1234, - expected_services: vec![ - ExpectedService::Exit( - irrelevant_public_key.clone(), - incoming_route_d_wallet.clone(), - rate_pack_d, - ), - ExpectedService::Routing( - irrelevant_public_key.clone(), - incoming_route_e_wallet.clone(), - rate_pack_e, - ), - ExpectedService::Routing( - irrelevant_public_key.clone(), - incoming_route_f_wallet.clone(), - rate_pack_f, + subject.stream_info.insert( + stream_key.clone(), + StreamInfoBuilder::new() + .dns_failure_retry(DNSFailureRetry { + unsuccessful_request: client_payload, + retries_left: 0, + }) + .route(RouteQueryResponse { + route: Route { hops: vec![] }, + expected_services: ExpectedServices::RoundTrip( + vec![], + vec![ + ExpectedService::Exit( + irrelevant_public_key.clone(), + incoming_route_d_wallet.clone(), + rate_pack_d, + ), + ExpectedService::Routing( + irrelevant_public_key.clone(), + incoming_route_e_wallet.clone(), + rate_pack_e, + ), + ExpectedService::Routing( + irrelevant_public_key.clone(), + incoming_route_f_wallet.clone(), + rate_pack_f, + ), + ExpectedService::Nothing, + ], ), - ExpectedService::Nothing, - ], - protocol: ProxyProtocol::TLS, - hostname_opt: Some("server.com".to_string()), - }, + host: Host::new("booga.com", TLS_PORT), + }) + .protocol(ProxyProtocol::TLS) + .build(), ); let subject_addr: Addr = subject.start(); let dns_resolve_failure_payload = DnsResolveFailure_0v1::new(stream_key); @@ -4659,7 +4908,7 @@ mod tests { ExpiredCoresPackage::new( SocketAddr::from_str("1.2.3.4:1234").unwrap(), Some(make_wallet("irrelevant")), - return_route_with_id(cryptde, 1234), + return_route(cryptde), dns_resolve_failure_payload.into(), 0, ); @@ -4724,34 +4973,34 @@ mod tests { ); let stream_key = StreamKey::make_meaningless_stream_key(); let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); - let mut dns_failure_retries_hash_map = HashMap::new(); let client_payload = make_request_payload(111, cryptde); - dns_failure_retries_hash_map.insert( - stream_key, - DNSFailureRetry { - unsuccessful_request: client_payload, - retries_left: 0, - }, - ); subject.logger = Logger::new(test_name); - subject.dns_failure_retries = dns_failure_retries_hash_map; 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( - 1234, - AddReturnRouteMessage { - return_route_id: 1234, - expected_services: vec![ExpectedService::Exit( - exit_public_key.clone(), - exit_wallet, - rate_pack(10), - )], - protocol: ProxyProtocol::HTTP, - hostname_opt: Some("server.com".to_string()), - }, + subject.stream_info.insert( + stream_key.clone(), + StreamInfoBuilder::new() + .dns_failure_retry(DNSFailureRetry { + unsuccessful_request: client_payload, + retries_left: 0, + }) + .route(RouteQueryResponse { + route: Route { hops: vec![] }, + expected_services: ExpectedServices::RoundTrip( + vec![], + vec![ExpectedService::Exit( + exit_public_key.clone(), + exit_wallet.clone(), + rate_pack(10), + )], + ), + host: Host::new("server.com", HTTP_PORT), + }) + .protocol(ProxyProtocol::HTTP) + .build(), ); let subject_addr: Addr = subject.start(); let dns_resolve_failure = DnsResolveFailure_0v1::new(stream_key); @@ -4759,7 +5008,7 @@ mod tests { ExpiredCoresPackage::new( SocketAddr::from_str("1.2.3.4:1234").unwrap(), Some(make_wallet("irrelevant")), - return_route_with_id(cryptde, 1234), + return_route(cryptde), dns_resolve_failure.into(), 0, ); @@ -4789,79 +5038,8 @@ mod tests { } #[test] - fn handle_dns_resolve_failure_does_not_send_message_to_neighborhood_when_server_is_not_specified( + fn handle_dns_resolve_failure_logs_when_stream_key_is_found_in_stream_info_but_not_keys_and_addrs( ) { - init_test_logging(); - let test_name = "handle_dns_resolve_failure_does_not_send_message_to_neighborhood_when_server_is_not_specified"; - let system = System::new(test_name); - let (neighborhood, _, neighborhood_recording_arc) = make_recorder(); - let cryptde = CRYPTDE_PAIR.main.as_ref(); - let mut subject = ProxyServer::new( - CRYPTDE_PAIR.clone(), - true, - Some(STANDARD_CONSUMING_WALLET_BALANCE), - false, - false, - ); - let stream_key = StreamKey::make_meaningless_stream_key(); - let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); - let mut dns_failure_retries_hash_map = HashMap::new(); - let client_payload = make_request_payload(111, cryptde); - dns_failure_retries_hash_map.insert( - stream_key, - DNSFailureRetry { - unsuccessful_request: client_payload, - retries_left: 0, - }, - ); - subject.logger = Logger::new(test_name); - subject.dns_failure_retries = dns_failure_retries_hash_map; - subject - .keys_and_addrs - .insert(stream_key.clone(), socket_addr); - let exit_public_key = PublicKey::from(&b"exit_key"[..]); - let exit_wallet = make_wallet("exit wallet"); - subject.route_ids_to_return_routes.insert( - 1234, - AddReturnRouteMessage { - return_route_id: 1234, - expected_services: vec![ExpectedService::Exit( - exit_public_key.clone(), - exit_wallet, - rate_pack(10), - )], - protocol: ProxyProtocol::HTTP, - hostname_opt: None, - }, - ); - 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(), - Some(make_wallet("irrelevant")), - return_route_with_id(cryptde, 1234), - dns_resolve_failure.into(), - 0, - ); - let peer_actors = peer_actors_builder().neighborhood(neighborhood).build(); - - 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_recording_arc.lock().unwrap(); - let record_opt = - neighborhood_recording.get_record_opt::(0); - assert_eq!(record_opt, None); - TestLogHandler::new().exists_log_containing(&format!( - "ERROR: {test_name}: Exit node {exit_public_key} complained of DNS failure, but was given no hostname to resolve." - )); - } - - #[test] - 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(); @@ -4874,43 +5052,39 @@ mod tests { false, ); let stream_key = StreamKey::make_meaningless_stream_key(); - let return_route_id = 1234; let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); - let mut dns_failure_retries_hash_map = HashMap::new(); let client_payload = make_request_payload(111, cryptde); - dns_failure_retries_hash_map.insert( - stream_key, - DNSFailureRetry { - unsuccessful_request: client_payload, - retries_left: 0, - }, - ); - subject.dns_failure_retries = dns_failure_retries_hash_map; - 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( - return_route_id, - AddReturnRouteMessage { - return_route_id, - expected_services: vec![ExpectedService::Exit( - exit_public_key.clone(), - exit_wallet, - rate_pack(10), - )], - protocol: ProxyProtocol::HTTP, - hostname_opt: Some("server.com".to_string()), - }, + subject.stream_info.insert( + stream_key, + StreamInfoBuilder::new() + .dns_failure_retry(DNSFailureRetry { + unsuccessful_request: client_payload, + retries_left: 0, + }) + .route(RouteQueryResponse { + route: Route { hops: vec![] }, + expected_services: ExpectedServices::RoundTrip( + vec![], + vec![ExpectedService::Exit( + exit_public_key.clone(), + exit_wallet.clone(), + rate_pack(10), + )], + ), + host: Host::new("server.com", HTTP_PORT), + }) + .protocol(ProxyProtocol::HTTP) + .build(), ); 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(), + socket_addr, Some(make_wallet("irrelevant")), - return_route_with_id(cryptde, return_route_id), + return_route(cryptde), dns_resolve_failure.into(), 0, ); @@ -4950,35 +5124,34 @@ mod tests { false, ); let stream_key = StreamKey::make_meaningless_stream_key(); - let return_route_id = 1234; let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); - let mut dns_failure_retries_hash_map = HashMap::new(); let client_payload = make_request_payload(111, cryptde); - dns_failure_retries_hash_map.insert( - stream_key, - DNSFailureRetry { - unsuccessful_request: client_payload, - retries_left: 0, - }, - ); - subject.dns_failure_retries = dns_failure_retries_hash_map; 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( - return_route_id, - AddReturnRouteMessage { - return_route_id, - expected_services: vec![ExpectedService::Exit( - exit_public_key.clone(), - exit_wallet, - rate_pack(10), - )], - protocol: ProxyProtocol::HTTP, - hostname_opt: None, - }, + subject.stream_info.insert( + stream_key, + StreamInfoBuilder::new() + .dns_failure_retry(DNSFailureRetry { + unsuccessful_request: client_payload, + retries_left: 0, + }) + .route(RouteQueryResponse { + route: Route { hops: vec![] }, + expected_services: ExpectedServices::RoundTrip( + vec![], + vec![ExpectedService::Exit( + exit_public_key.clone(), + exit_wallet.clone(), + rate_pack(10), + )], + ), + host: Host::new("booga.com", HTTP_PORT), + }) + .protocol(ProxyProtocol::HTTP) + .build(), ); let subject_addr: Addr = subject.start(); let dns_resolve_failure = DnsResolveFailure_0v1::new(stream_key); @@ -4986,7 +5159,7 @@ mod tests { ExpiredCoresPackage::new( SocketAddr::from_str("1.2.3.4:1234").unwrap(), Some(make_wallet("irrelevant")), - return_route_with_id(cryptde, return_route_id), + return_route(cryptde), dns_resolve_failure.into(), 0, ); @@ -5003,12 +5176,10 @@ mod tests { System::current().stop(); system.run(); - TestLogHandler::new().exists_log_containing( - &format!( - "Discarding DnsResolveFailure message for from an unrecognized stream key {:?}", - stream_key - ) - ); + TestLogHandler::new().exists_log_containing(&format!( + "Discarding DnsResolveFailure message from an unrecognized stream key {:?}", + stream_key + )); } #[test] @@ -5033,47 +5204,38 @@ mod tests { subject.subs.as_mut().unwrap().dispatcher = peer_actors.dispatcher.from_dispatcher_client; let stream_key = StreamKey::make_meaningless_stream_key(); let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); - let mut dns_failure_retries_hash_map = HashMap::new(); let client_payload = make_request_payload(111, cryptde); - dns_failure_retries_hash_map.insert( - stream_key, - DNSFailureRetry { - unsuccessful_request: client_payload, - retries_left: 0, - }, - ); - subject.dns_failure_retries = dns_failure_retries_hash_map; subject .keys_and_addrs .insert(stream_key.clone(), socket_addr.clone()); - subject - .tunneled_hosts - .insert(stream_key.clone(), "tunneled host".to_string()); - subject.stream_key_routes.insert( - stream_key.clone(), - RouteQueryResponse { - route: Route { hops: vec![] }, - expected_services: ExpectedServices::OneWay(vec![]), - }, - ); - subject.route_ids_to_return_routes.insert( - 1234, - AddReturnRouteMessage { - return_route_id: 1234, - expected_services: vec![ - make_exit_service_from_key(PublicKey::new(b"exit_node")), - ExpectedService::Nothing, - ], - protocol: ProxyProtocol::HTTP, - hostname_opt: None, - }, + subject.stream_info.insert( + stream_key, + StreamInfoBuilder::new() + .dns_failure_retry(DNSFailureRetry { + unsuccessful_request: client_payload, + retries_left: 0, + }) + .tunneled_host("tunneled host") + .route(RouteQueryResponse { + route: Route { hops: vec![] }, + expected_services: ExpectedServices::RoundTrip( + vec![], + vec![ + make_exit_service_from_key(PublicKey::new(b"exit_node")), + ExpectedService::Nothing, + ], + ), + host: Host::new("booga.com", HTTP_PORT), + }) + .protocol(ProxyProtocol::HTTP) + .build(), ); 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(), Some(make_wallet("irrelevant")), - return_route_with_id(cryptde, 1234), + return_route(cryptde), dns_resolve_failure.into(), 0, ); @@ -5081,8 +5243,7 @@ mod tests { subject.handle_dns_resolve_failure(&expired_cores_package); assert!(subject.keys_and_addrs.is_empty()); - assert!(subject.stream_key_routes.is_empty()); - assert!(subject.tunneled_hosts.is_empty()); + assert!(subject.stream_info.get(&stream_key).is_none()); } #[test] @@ -5101,27 +5262,27 @@ mod tests { ); let stream_key = StreamKey::make_meaningless_stream_key(); let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); - let mut dns_failure_retries_hash_map = HashMap::new(); let client_payload = make_request_payload(111, cryptde); - dns_failure_retries_hash_map.insert( - stream_key, - DNSFailureRetry { - unsuccessful_request: client_payload, - retries_left: 0, - }, - ); - subject.dns_failure_retries = dns_failure_retries_hash_map; subject .keys_and_addrs .insert(stream_key.clone(), socket_addr.clone()); - subject.route_ids_to_return_routes.insert( - 1234, - AddReturnRouteMessage { - return_route_id: 1234, - expected_services: vec![ExpectedService::Nothing, ExpectedService::Nothing], - protocol: ProxyProtocol::HTTP, - hostname_opt: Some("server.com".to_string()), - }, + subject.stream_info.insert( + stream_key.clone(), + StreamInfoBuilder::new() + .dns_failure_retry(DNSFailureRetry { + unsuccessful_request: client_payload, + retries_left: 0, + }) + .route(RouteQueryResponse { + route: Route { hops: vec![] }, + expected_services: ExpectedServices::RoundTrip( + vec![], + vec![ExpectedService::Nothing, ExpectedService::Nothing], + ), + host: Host::new("server.com", HTTP_PORT), + }) + .protocol(ProxyProtocol::HTTP) + .build(), ); let subject_addr: Addr = subject.start(); let dns_resolve_failure = DnsResolveFailure_0v1::new(stream_key); @@ -5129,7 +5290,7 @@ mod tests { ExpiredCoresPackage::new( SocketAddr::from_str("1.2.3.4:1234").unwrap(), Some(make_wallet("irrelevant")), - return_route_with_id(cryptde, 1234), + return_route(cryptde), dns_resolve_failure.into(), 0, ); @@ -5160,9 +5321,9 @@ mod tests { TransmitDataMsg { endpoint: Endpoint::Socket(socket_addr), last_data: true, - sequence_number: Some(0), + sequence_number_opt: Some(0), data: ServerImpersonatorHttp {} - .dns_resolution_failure_response(Some("server.com".to_string()),), + .dns_resolution_failure_response("server.com".to_string()), }, *record ); @@ -5170,7 +5331,8 @@ mod tests { #[test] fn handle_dns_resolve_failure_sent_request_retry() { - let system = System::new("test"); + let test_name = "handle_dns_resolve_failure_sent_request_retry"; + let system = System::new(test_name); let resolve_message_params_arc = Arc::new(Mutex::new(vec![])); let (neighborhood_mock, _, _) = make_recorder(); let exit_public_key = PublicKey::from(&b"exit_key"[..]); @@ -5185,8 +5347,8 @@ mod tests { expected_services: ExpectedServices::RoundTrip( expected_services.clone(), expected_services.clone(), - 1234, ), + host: Host::new("booga.com", HTTP_PORT), }; let neighborhood_mock = neighborhood_mock .system_stop_conditions(match_every_type_id!(RouteQueryMessage)) @@ -5200,28 +5362,28 @@ mod tests { false, ); let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); - let mut dns_failure_retries_hash_map = HashMap::new(); let client_payload = make_request_payload(111, cryptde); let stream_key = client_payload.stream_key; - dns_failure_retries_hash_map.insert( - stream_key, - DNSFailureRetry { - unsuccessful_request: client_payload.clone(), - retries_left: 3, - }, - ); - subject.dns_failure_retries = dns_failure_retries_hash_map; subject .keys_and_addrs .insert(stream_key.clone(), socket_addr.clone()); - subject.route_ids_to_return_routes.insert( - 1234, - AddReturnRouteMessage { - return_route_id: 1234, - expected_services: expected_services.clone(), - protocol: ProxyProtocol::HTTP, - hostname_opt: Some("server.com".to_string()), - }, + subject.stream_info.insert( + stream_key.clone(), + StreamInfoBuilder::new() + .dns_failure_retry(DNSFailureRetry { + unsuccessful_request: client_payload.clone(), + retries_left: 3, + }) + .route(RouteQueryResponse { + route: Route { hops: vec![] }, + expected_services: ExpectedServices::RoundTrip( + vec![], + expected_services.clone(), + ), + host: Host::new("server.com", HTTP_PORT), + }) + .protocol(ProxyProtocol::HTTP) + .build(), ); let message_resolver = RouteQueryResponseResolverMock::default() .resolve_message_params(&resolve_message_params_arc); @@ -5236,7 +5398,7 @@ mod tests { ExpiredCoresPackage::new( SocketAddr::from_str("1.2.3.4:1234").unwrap(), Some(make_wallet("irrelevant")), - return_route_with_id(cryptde, 1234), + return_route(cryptde), dns_resolve_failure.into(), 0, ); @@ -5250,7 +5412,12 @@ mod tests { subject_addr .try_send(AssertionsMessage { assertions: Box::new(move |proxy_server: &mut ProxyServer| { - let retry = proxy_server.dns_failure_retries.get(&stream_key).unwrap(); + let retry = proxy_server + .stream_info(&stream_key) + .unwrap() + .dns_failure_retry_opt + .as_ref() + .unwrap(); assert_eq!(retry.retries_left, 2); }), }) @@ -5275,10 +5442,10 @@ mod tests { } #[test] - fn handle_dns_resolve_failure_logs_error_when_there_is_no_entry_in_the_hashmap_for_the_stream_key( + fn handle_dns_resolve_failure_logs_error_when_there_is_no_dns_failure_retry_entry_for_the_stream_key( ) { init_test_logging(); - let test_name = "handle_dns_resolve_failure_logs_error_when_there_is_no_entry_in_the_hashmap_for_the_stream_key"; + let test_name = "handle_dns_resolve_failure_logs_error_when_there_is_no_dns_failure_retry_entry_for_the_stream_key"; let system = System::new(test_name); let exit_public_key = PublicKey::from(&b"exit_key"[..]); let exit_wallet = make_wallet("exit wallet"); @@ -5301,14 +5468,19 @@ mod tests { subject .keys_and_addrs .insert(stream_key.clone(), socket_addr.clone()); - subject.route_ids_to_return_routes.insert( - 1234, - AddReturnRouteMessage { - return_route_id: 1234, - expected_services: expected_services.clone(), - protocol: ProxyProtocol::HTTP, - hostname_opt: Some("server.com".to_string()), - }, + subject.stream_info.insert( + stream_key.clone(), + StreamInfoBuilder::new() + .route(RouteQueryResponse { + route: Route { hops: vec![] }, + expected_services: ExpectedServices::RoundTrip( + vec![], + expected_services.clone(), + ), + host: Host::new("server.com", HTTP_PORT), + }) + .protocol(ProxyProtocol::HTTP) + .build(), ); let subject_addr: Addr = subject.start(); let dns_resolve_failure = DnsResolveFailure_0v1::new(stream_key); @@ -5316,7 +5488,7 @@ mod tests { ExpiredCoresPackage::new( SocketAddr::from_str("1.2.3.4:1234").unwrap(), Some(make_wallet("irrelevant")), - return_route_with_id(cryptde, 1234), + return_route(cryptde), dns_resolve_failure.into(), 0, ); @@ -5329,8 +5501,7 @@ mod tests { system.run(); TestLogHandler::new().exists_log_containing(&format!( "ERROR: {test_name}: While \ - handling ExpiredCoresPackage: No entry found inside dns_failure_retries hashmap for \ - the stream_key: AAAAAAAAAAAAAAAAAAAAAAAAAAA" + handling ExpiredCoresPackage: No DNSFailureRetry entry found for the stream_key: {stream_key}" )); } @@ -5353,8 +5524,8 @@ mod tests { expected_services: ExpectedServices::RoundTrip( expected_services.clone(), expected_services.clone(), - 1234, ), + host: Host::new("booga.com", HTTP_PORT), }; let neighborhood_mock = neighborhood_mock .system_stop_conditions(match_every_type_id!( @@ -5373,29 +5544,29 @@ mod tests { ); subject.logger = Logger::new(test_name); let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); - let mut dns_failure_retries_hash_map = HashMap::new(); let client_payload = make_request_payload(111, cryptde); let stream_key = client_payload.stream_key; let stream_key_clone = stream_key.clone(); - dns_failure_retries_hash_map.insert( - stream_key, - DNSFailureRetry { - unsuccessful_request: client_payload.clone(), - retries_left: 3, - }, - ); - subject.dns_failure_retries = dns_failure_retries_hash_map; subject .keys_and_addrs .insert(stream_key.clone(), socket_addr.clone()); - subject.route_ids_to_return_routes.insert( - 1234, - AddReturnRouteMessage { - return_route_id: 1234, - expected_services: expected_services.clone(), - protocol: ProxyProtocol::HTTP, - hostname_opt: Some("server.com".to_string()), - }, + subject.stream_info.insert( + stream_key_clone.clone(), + StreamInfoBuilder::new() + .dns_failure_retry(DNSFailureRetry { + unsuccessful_request: client_payload, + retries_left: 3, + }) + .route(RouteQueryResponse { + route: Route { hops: vec![] }, + expected_services: ExpectedServices::RoundTrip( + vec![], + expected_services.clone(), + ), + host: Host::new("server.com", HTTP_PORT), + }) + .protocol(ProxyProtocol::HTTP) + .build(), ); let message_resolver_factory = RouteQueryResponseResolverFactoryMock::default() .make_params(&make_params_arc) @@ -5412,7 +5583,7 @@ mod tests { ExpiredCoresPackage::new( SocketAddr::from_str("1.2.3.4:1234").unwrap(), Some(make_wallet("irrelevant")), - return_route_with_id(cryptde, 1234), + return_route(cryptde), dns_resolve_failure.into(), 0, ); @@ -5436,9 +5607,7 @@ mod tests { .try_send(AssertionsMessage { assertions: Box::new(move |proxy_server: &mut ProxyServer| { assert_eq!(proxy_server.keys_and_addrs.a_to_b(&stream_key), None); - assert_eq!(proxy_server.stream_key_routes.get(&stream_key), None); - assert_eq!(proxy_server.tunneled_hosts.get(&stream_key), None); - assert_eq!(proxy_server.dns_failure_retries.get(&stream_key), None); + assert_eq!(proxy_server.stream_info.get(&stream_key).is_none(), true); }), }) .unwrap(); @@ -5467,15 +5636,20 @@ mod tests { subject .keys_and_addrs .insert(stream_key.clone(), socket_addr.clone()); - let remaining_route = return_route_with_id(cryptde, 4321); - subject.route_ids_to_return_routes.insert( - 4321, - AddReturnRouteMessage { - return_route_id: 4321, - expected_services: vec![ExpectedService::Nothing], - protocol: ProxyProtocol::HTTP, - hostname_opt: None, - }, + let remaining_route = return_route(cryptde); + subject.stream_info.insert( + stream_key.clone(), + StreamInfoBuilder::new() + .route(RouteQueryResponse { + route: Route { hops: vec![] }, + expected_services: ExpectedServices::RoundTrip( + vec![], + vec![ExpectedService::Nothing], + ), + host: Host::new("booga.com", HTTP_PORT), + }) + .protocol(ProxyProtocol::HTTP) + .build(), ); let subject_addr: Addr = subject.start(); @@ -5518,8 +5692,8 @@ mod tests { let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr.clone(), - reception_port: Some(80), - sequence_number: Some(0), + reception_port_opt: Some(80), + sequence_number_opt: Some(0), last_data: false, is_clandestine: false, data: expected_data.clone(), @@ -5557,57 +5731,7 @@ mod tests { .accountant(accountant) .build(); let client_response_payload = ClientResponsePayload_0v1 { - stream_key, - sequenced_packet: SequencedPacket { - data: b"some data".to_vec(), - sequence_number: 4321, - last_data: false, - }, - }; - let expired_cores_package = ExpiredCoresPackage::new( - SocketAddr::from_str("1.2.3.4:1234").unwrap(), - Some(make_wallet("irrelevant")), - return_route_with_id(cryptde, 1234), - client_response_payload, - 0, - ); - subject_addr.try_send(BindMessage { peer_actors }).unwrap(); - - subject_addr.try_send(expired_cores_package).unwrap(); - - System::current().stop(); - system.run(); - TestLogHandler::new().exists_log_containing("ERROR: ProxyServer: Can't report services consumed: received response with bogus return-route ID 1234 for client response. Ignoring"); - assert_eq!(dispatcher_recording_arc.lock().unwrap().len(), 0); - assert_eq!(accountant_recording_arc.lock().unwrap().len(), 0); - } - - #[test] - fn report_response_services_consumed_complains_and_drops_package_if_return_route_id_is_unreadable( - ) { - init_test_logging(); - let cryptde = CRYPTDE_PAIR.main.as_ref(); - let (dispatcher, _, dispatcher_recording_arc) = make_recorder(); - let (accountant, _, accountant_recording_arc) = make_recorder(); - let system = System::new("report_response_services_consumed_complains_and_drops_package_if_return_route_id_is_unreadable"); - let mut subject = ProxyServer::new( - CRYPTDE_PAIR.clone(), - true, - Some(STANDARD_CONSUMING_WALLET_BALANCE), - false, - false, - ); - let stream_key = StreamKey::make_meaningless_stream_key(); - subject - .keys_and_addrs - .insert(stream_key, SocketAddr::from_str("1.2.3.4:5678").unwrap()); - let subject_addr: Addr = subject.start(); - let peer_actors = peer_actors_builder() - .dispatcher(dispatcher) - .accountant(accountant) - .build(); - let client_response_payload = ClientResponsePayload_0v1 { - stream_key, + stream_key: stream_key.clone(), sequenced_packet: SequencedPacket { data: b"some data".to_vec(), sequence_number: 4321, @@ -5617,9 +5741,7 @@ mod tests { let expired_cores_package = ExpiredCoresPackage::new( SocketAddr::from_str("1.2.3.4:1234").unwrap(), Some(make_wallet("irrelevant")), - Route { - hops: vec![make_cover_hop(cryptde), CryptData::new(&[0])], - }, + return_route(cryptde), client_response_payload, 0, ); @@ -5629,74 +5751,11 @@ mod tests { System::current().stop(); system.run(); - TestLogHandler::new().exists_log_containing( - "ERROR: ProxyServer: Can't report services consumed: DecryptionError(InvalidKey(\"Could not decrypt with", - ); + TestLogHandler::new().exists_log_containing(format!("ERROR: ProxyServer: Can't pay for return services consumed: received response with unrecognized stream key {}. Ignoring", stream_key).as_str()); assert_eq!(dispatcher_recording_arc.lock().unwrap().len(), 0); assert_eq!(accountant_recording_arc.lock().unwrap().len(), 0); } - #[test] - fn return_route_ids_expire_when_instructed() { - init_test_logging(); - let cryptde = CRYPTDE_PAIR.main.as_ref(); - let stream_key = StreamKey::make_meaningless_stream_key(); - - let (tx, rx) = unbounded(); - thread::spawn(move || { - let system = System::new("report_response_services_consumed_complains_and_drops_package_if_return_route_id_does_not_exist"); - let mut subject = ProxyServer::new( - CRYPTDE_PAIR.clone(), - true, - Some(STANDARD_CONSUMING_WALLET_BALANCE), - false, - false, - ); - subject.route_ids_to_return_routes = TtlHashMap::new(Duration::from_millis(250)); - subject - .keys_and_addrs - .insert(stream_key, SocketAddr::from_str("1.2.3.4:5678").unwrap()); - subject.route_ids_to_return_routes.insert( - 1234, - AddReturnRouteMessage { - return_route_id: 1234, - expected_services: vec![], - protocol: ProxyProtocol::TLS, - hostname_opt: None, - }, - ); - let subject_addr: Addr = subject.start(); - let peer_actors = peer_actors_builder().build(); - subject_addr.try_send(BindMessage { peer_actors }).unwrap(); - tx.send(subject_addr).unwrap(); - - system.run(); - }); - - let subject_addr = rx.recv().unwrap(); - - thread::sleep(Duration::from_millis(300)); - - let client_response_payload = ClientResponsePayload_0v1 { - stream_key, - sequenced_packet: SequencedPacket { - data: b"some data".to_vec(), - sequence_number: 4321, - last_data: false, - }, - }; - let expired_cores_package = ExpiredCoresPackage::new( - SocketAddr::from_str("1.2.3.4:1234").unwrap(), - Some(make_wallet("irrelevant")), - return_route_with_id(cryptde, 1234), - client_response_payload, - 0, - ); - subject_addr.try_send(expired_cores_package).unwrap(); - - TestLogHandler::new().await_log_containing("ERROR: ProxyServer: Can't report services consumed: received response with bogus return-route ID 1234 for client response. Ignoring", 1000); - } - #[test] fn handle_stream_shutdown_msg_handles_unknown_peer_addr() { let mut subject = ProxyServer::new(CRYPTDE_PAIR.clone(), true, None, false, false); @@ -5705,16 +5764,17 @@ mod tests { subject .keys_and_addrs .insert(unaffected_stream_key, unaffected_socket_addr); - subject.stream_key_routes.insert( + subject.stream_info.insert( unaffected_stream_key, - RouteQueryResponse { - route: Route { hops: vec![] }, - expected_services: ExpectedServices::RoundTrip(vec![], vec![], 1234), - }, + StreamInfoBuilder::new() + .route(RouteQueryResponse { + route: Route { hops: vec![] }, + expected_services: ExpectedServices::RoundTrip(vec![], vec![]), + host: Host::new("booga.com", HTTP_PORT), + }) + .tunneled_host("blah") + .build(), ); - subject - .tunneled_hosts - .insert(unaffected_stream_key, "blah".to_string()); subject.handle_stream_shutdown_msg(StreamShutdownMsg { peer_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), @@ -5730,10 +5790,12 @@ mod tests { .keys_and_addrs .a_to_b(&unaffected_stream_key) .is_some()); + assert!(subject.stream_info.contains_key(&unaffected_stream_key)); assert!(subject - .stream_key_routes - .contains_key(&unaffected_stream_key)); - assert!(subject.tunneled_hosts.contains_key(&unaffected_stream_key)); + .stream_info(&unaffected_stream_key) + .unwrap() + .tunneled_host_opt + .is_some()); } #[test] @@ -5757,12 +5819,16 @@ mod tests { subject .keys_and_addrs .insert(affected_stream_key, affected_socket_addr); - subject.stream_key_routes.insert( + subject.stream_info.insert( unaffected_stream_key, - RouteQueryResponse { - route: Route { hops: vec![] }, - expected_services: ExpectedServices::RoundTrip(vec![], vec![], 1234), - }, + StreamInfoBuilder::new() + .route(RouteQueryResponse { + route: Route { hops: vec![] }, + expected_services: ExpectedServices::RoundTrip(vec![], vec![]), + host: Host::new("gooba.com", HTTP_PORT), + }) + .tunneled_host("blah") + .build(), ); let affected_route = Route::round_trip( RouteSegment::new( @@ -5781,7 +5847,6 @@ mod tests { ), CRYPTDE_PAIR.main.as_ref(), Some(make_paying_wallet(b"consuming")), - 1234, Some(TEST_DEFAULT_CHAIN.rec().contract), ) .unwrap(); @@ -5790,23 +5855,20 @@ mod tests { make_paying_wallet(b"1234"), DEFAULT_RATE_PACK, )]; - subject.stream_key_routes.insert( + subject.stream_info.insert( affected_stream_key, - RouteQueryResponse { - route: affected_route.clone(), - expected_services: ExpectedServices::RoundTrip( - affected_expected_services, - vec![], - 1234, - ), - }, + StreamInfoBuilder::new() + .route(RouteQueryResponse { + route: affected_route.clone(), + expected_services: ExpectedServices::RoundTrip( + affected_expected_services, + vec![], + ), + host: Host::new("gooba.com", TLS_PORT), + }) + .tunneled_host("tunneled.com") + .build(), ); - subject - .tunneled_hosts - .insert(unaffected_stream_key, "blah".to_string()); - subject - .tunneled_hosts - .insert(affected_stream_key, "tunneled.com".to_string()); let subject_addr = subject.start(); let (hopper, _, hopper_recording_arc) = make_recorder(); let (proxy_server, _, proxy_server_recording_arc) = make_recorder(); @@ -5840,8 +5902,8 @@ mod tests { ClientRequestPayload_0v1 { stream_key: affected_stream_key, sequenced_packet: SequencedPacket::new(vec![], 1234, true), - target_hostname: Some(String::from("tunneled.com")), - target_port: 443, + target_hostname: String::from("tunneled.com"), + target_port: TLS_PORT, protocol: ProxyProtocol::TLS, originator_public_key: CRYPTDE_PAIR.alias.as_ref().public_key().clone(), } @@ -5887,12 +5949,15 @@ mod tests { subject .keys_and_addrs .insert(affected_stream_key, affected_socket_addr); - subject.stream_key_routes.insert( + subject.stream_info.insert( unaffected_stream_key, - RouteQueryResponse { - route: Route { hops: vec![] }, - expected_services: ExpectedServices::RoundTrip(vec![], vec![], 1234), - }, + StreamInfoBuilder::new() + .route(RouteQueryResponse { + route: Route { hops: vec![] }, + expected_services: ExpectedServices::RoundTrip(vec![], vec![]), + host: Host::new("booga.com", HTTP_PORT), + }) + .build(), ); let affected_route = Route::round_trip( RouteSegment::new( @@ -5911,7 +5976,6 @@ mod tests { ), CRYPTDE_PAIR.main.as_ref(), Some(make_paying_wallet(b"consuming")), - 1234, Some(TEST_DEFAULT_CHAIN.rec().contract), ) .unwrap(); @@ -5920,16 +5984,18 @@ mod tests { make_paying_wallet(b"1234"), DEFAULT_RATE_PACK, )]; - subject.stream_key_routes.insert( + subject.stream_info.insert( affected_stream_key, - RouteQueryResponse { - route: affected_route.clone(), - expected_services: ExpectedServices::RoundTrip( - affected_expected_services, - vec![], - 1234, - ), - }, + StreamInfoBuilder::new() + .route(RouteQueryResponse { + route: affected_route.clone(), + expected_services: ExpectedServices::RoundTrip( + affected_expected_services, + vec![], + ), + host: Host::new("booga.com", HTTP_PORT), + }) + .build(), ); subject.logger = Logger::new(test_name); let subject_addr = subject.start(); @@ -5965,7 +6031,7 @@ mod tests { ClientRequestPayload_0v1 { stream_key: affected_stream_key, sequenced_packet: SequencedPacket::new(vec![], 1234, true), - target_hostname: None, + target_hostname: "booga.com".to_string(), target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: CRYPTDE_PAIR.alias.as_ref().public_key().clone(), @@ -6004,6 +6070,9 @@ mod tests { let socket_addr = SocketAddr::from_str("3.4.5.6:7777").unwrap(); let stream_key = StreamKey::make_meaningful_stream_key("All Things Must Pass"); subject.keys_and_addrs.insert(stream_key, socket_addr); + subject + .stream_info + .insert(stream_key.clone(), StreamInfoBuilder::new().build()); let msg = StreamShutdownMsg { peer_addr: socket_addr, stream_type: RemovedStreamType::NonClandestine(NonClandestineAttributes { @@ -6030,16 +6099,17 @@ mod tests { let socket_addr = SocketAddr::from_str("3.4.5.6:7890").unwrap(); let stream_key = StreamKey::make_meaningful_stream_key("All Things Must Pass"); subject.keys_and_addrs.insert(stream_key, socket_addr); - subject.stream_key_routes.insert( + subject.stream_info.insert( stream_key, - RouteQueryResponse { - route: Route { hops: vec![] }, - expected_services: ExpectedServices::RoundTrip(vec![], vec![], 0), - }, + StreamInfoBuilder::new() + .route(RouteQueryResponse { + route: Route { hops: vec![] }, + expected_services: ExpectedServices::RoundTrip(vec![], vec![]), + host: Host::new("booga.com", HTTP_PORT), + }) + .tunneled_host("blah") + .build(), ); - subject - .tunneled_hosts - .insert(stream_key, "blah".to_string()); let msg = StreamShutdownMsg { peer_addr: socket_addr, stream_type: RemovedStreamType::NonClandestine(NonClandestineAttributes { @@ -6055,14 +6125,13 @@ mod tests { 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]; + let inbound_client_data_msg = &handle_normal_client_data[0]; assert_eq!(inbound_client_data_msg.client_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] @@ -6072,18 +6141,15 @@ mod tests { let inbound_client_data_msg = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:4578").unwrap(), - reception_port: None, + reception_port_opt: None, last_data: true, is_clandestine: false, - sequence_number: Some(123), + sequence_number_opt: Some(123), data: vec![], }; - let result = IBCDHelperReal::new().handle_normal_client_data( - &mut proxy_server, - inbound_client_data_msg, - true, - ); + let result = IBCDHelperReal::new() + .handle_normal_client_data(&mut proxy_server, inbound_client_data_msg); assert_eq!( result, @@ -6111,16 +6177,10 @@ mod tests { accountant_sub: recipient!(&addr, ReportServicesConsumedMessage), retire_stream_key_sub_opt: None, }; - let add_return_route_sub = recipient!(&addr, AddReturnRouteMessage); let subject = RouteQueryResponseResolverReal {}; let system = System::new("resolve_message_handles_mailbox_error_from_neighborhood"); - subject.resolve_message( - args, - add_return_route_sub, - proxy_server_sub, - Err(MailboxError::Timeout), - ); + subject.resolve_message(args, proxy_server_sub, Err(MailboxError::Timeout)); System::current().stop(); system.run(); @@ -6138,22 +6198,63 @@ mod tests { #[derive(Default)] struct ClientRequestPayloadFactoryMock { + make_params: Arc< + Mutex< + Vec<( + InboundClientData, + StreamKey, + Option, + Box, + Logger, + )>, + >, + >, make_results: RefCell>>, } impl ClientRequestPayloadFactory for ClientRequestPayloadFactoryMock { fn make( &self, - _ibcd: &InboundClientData, - _stream_key: StreamKey, - _cryptde: &dyn CryptDE, - _logger: &Logger, + ibcd: &InboundClientData, + stream_key: StreamKey, + host_opt: Option, + cryptde: &dyn CryptDE, + logger: &Logger, ) -> Option { + self.make_params.lock().unwrap().push(( + ibcd.clone(), + stream_key, + host_opt, + cryptde.dup(), + logger.clone(), + )); self.make_results.borrow_mut().remove(0) } } impl ClientRequestPayloadFactoryMock { + fn new() -> Self { + Self::default() + } + + fn make_params( + mut self, + params: &Arc< + Mutex< + Vec<( + InboundClientData, + StreamKey, + Option, + Box, + Logger, + )>, + >, + >, + ) -> Self { + self.make_params = params.clone(); + self + } + fn make_result(self, result: Option) -> Self { self.make_results.borrow_mut().push(result); self @@ -6175,18 +6276,15 @@ mod tests { let inbound_client_data_msg = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:4578").unwrap(), - reception_port: Some(568), + reception_port_opt: Some(568), last_data: true, is_clandestine: false, - sequence_number: Some(123), + sequence_number_opt: Some(123), data: vec![], }; - let result = IBCDHelperReal::new().handle_normal_client_data( - &mut proxy_server, - inbound_client_data_msg, - true, - ); + let result = IBCDHelperReal::new() + .handle_normal_client_data(&mut proxy_server, inbound_client_data_msg); assert_eq!( result, @@ -6196,7 +6294,7 @@ mod tests { #[test] fn new_http_request_creates_new_entry_inside_dns_retries_hashmap() { - let alias_cryptde = CRYPTDE_PAIR.alias.as_ref(); + let test_name = "new_http_request_creates_new_entry_inside_dns_retries_hashmap"; let http_request = b"GET /index.html HTTP/1.1\r\nHost: nowhere.com\r\n\r\n"; let (neighborhood_mock, _, _) = make_recorder(); let destination_key = PublicKey::from(&b"our destination"[..]); @@ -6205,8 +6303,8 @@ mod tests { expected_services: ExpectedServices::RoundTrip( vec![make_exit_service_from_key(destination_key.clone())], vec![], - 1234, ), + host: Host::new("booga.com", HTTP_PORT), })); let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); let stream_key = StreamKey::make_meaningless_stream_key(); @@ -6214,8 +6312,8 @@ mod tests { let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr.clone(), - reception_port: Some(HTTP_PORT), - sequence_number: Some(0), + reception_port_opt: Some(HTTP_PORT), + sequence_number_opt: Some(0), last_data: true, is_clandestine: false, data: expected_data.clone(), @@ -6224,14 +6322,13 @@ mod tests { .make( &msg_from_dispatcher, stream_key.clone(), - alias_cryptde, + None, + CRYPTDE_PAIR.alias.as_ref(), &Logger::new("test"), ) .unwrap(); let stream_key_factory = StreamKeyFactoryMock::new().make_result(stream_key.clone()); - let system = System::new( - "proxy_server_receives_http_request_from_dispatcher_then_sends_cores_package_to_hopper", - ); + let system = System::new(test_name); let mut subject = ProxyServer::new( CRYPTDE_PAIR.clone(), true, @@ -6251,7 +6348,12 @@ mod tests { subject_addr .try_send(AssertionsMessage { assertions: Box::new(move |proxy_server: &mut ProxyServer| { - let dns_retry = proxy_server.dns_failure_retries.get(&stream_key).unwrap(); + let dns_retry = proxy_server + .stream_info(&stream_key) + .unwrap() + .dns_failure_retry_opt + .as_ref() + .unwrap(); assert_eq!(dns_retry.retries_left, 3); assert_eq!(dns_retry.unsuccessful_request, expected_payload); }), @@ -6263,6 +6365,8 @@ mod tests { #[test] fn new_http_request_creates_new_exhausted_entry_inside_dns_retries_hashmap_zero_hop() { + let test_name = + "new_http_request_creates_new_exhausted_entry_inside_dns_retries_hashmap_zero_hop"; let http_request = b"GET /index.html HTTP/1.1\r\nHost: nowhere.com\r\n\r\n"; let (neighborhood_mock, _, _) = make_recorder(); let destination_key = PublicKey::from(&b"our destination"[..]); @@ -6271,8 +6375,8 @@ mod tests { expected_services: ExpectedServices::RoundTrip( vec![make_exit_service_from_key(destination_key.clone())], vec![], - 1234, ), + host: Host::new("booga.com", HTTP_PORT), })); let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); let stream_key = StreamKey::make_meaningless_stream_key(); @@ -6280,8 +6384,8 @@ mod tests { let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr.clone(), - reception_port: Some(HTTP_PORT), - sequence_number: Some(0), + reception_port_opt: Some(HTTP_PORT), + sequence_number_opt: Some(0), last_data: true, is_clandestine: false, data: expected_data.clone(), @@ -6290,14 +6394,13 @@ mod tests { .make( &msg_from_dispatcher, stream_key.clone(), + None, CRYPTDE_PAIR.alias.as_ref(), &Logger::new("test"), ) .unwrap(); let stream_key_factory = StreamKeyFactoryMock::new().make_result(stream_key.clone()); - let system = System::new( - "new_http_request_creates_new_exhausted_entry_inside_dns_retries_hashmap_zero_hop", - ); + let system = System::new(test_name); let mut subject = ProxyServer::new( CRYPTDE_PAIR.clone(), false, @@ -6317,7 +6420,12 @@ mod tests { subject_addr .try_send(AssertionsMessage { assertions: Box::new(move |proxy_server: &mut ProxyServer| { - let dns_retry = proxy_server.dns_failure_retries.get(&stream_key).unwrap(); + let dns_retry = proxy_server + .stream_info(&stream_key) + .unwrap() + .dns_failure_retry_opt + .as_ref() + .unwrap(); assert_eq!(dns_retry.retries_left, 0); assert_eq!(dns_retry.unsuccessful_request, expected_payload); }), @@ -6407,18 +6515,15 @@ mod tests { let inbound_client_data_msg = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:4578").unwrap(), - reception_port: Some(80), + reception_port_opt: Some(80), last_data: true, is_clandestine: false, - sequence_number: Some(123), + sequence_number_opt: Some(123), data: expected_data, }; - let result = IBCDHelperReal::new().handle_normal_client_data( - &mut proxy_server, - inbound_client_data_msg, - true, - ); + let result = IBCDHelperReal::new() + .handle_normal_client_data(&mut proxy_server, inbound_client_data_msg); assert_eq!( result, @@ -6426,6 +6531,78 @@ mod tests { ); } + #[test] + fn make_payload_passes_no_hostname_if_none_is_known() { + let mut subject = ProxyServer::new(CRYPTDE_PAIR.clone(), true, Some(58), false, false); + let make_params_arc = Arc::new(Mutex::new(vec![])); + let client_request_payload_factory = ClientRequestPayloadFactoryMock::new() + .make_params(&make_params_arc) + .make_result(None); + subject.client_request_payload_factory = Box::new(client_request_payload_factory); + let stream_key = StreamKey::make_meaningless_stream_key(); + // Do not create an entry in subject.stream_info for stream_key, so that no hostname is known + + let _ = subject.make_payload( + InboundClientData { + // irrelevant + timestamp: SystemTime::now(), + client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), + reception_port_opt: Some(HTTP_PORT), + last_data: false, + is_clandestine: false, + sequence_number_opt: Some(123), + data: vec![], + }, + &stream_key, + ); + + let (_ibcd, _sk, hostname_opt, _cryptde, _logger) = &make_params_arc.lock().unwrap()[0]; + assert_eq!(hostname_opt, &None); + } + + #[test] + fn make_payload_passes_hostname_if_known() { + let mut subject = ProxyServer::new(CRYPTDE_PAIR.clone(), true, Some(58), false, false); + let make_params_arc = Arc::new(Mutex::new(vec![])); + let client_request_payload_factory = ClientRequestPayloadFactoryMock::new() + .make_params(&make_params_arc) + .make_result(None); // Don't care about return value, only parameters + subject.client_request_payload_factory = Box::new(client_request_payload_factory); + let stream_key = StreamKey::make_meaningless_stream_key(); + let si_host = Host::new("knownhostname.com", 2345); + subject.stream_info.insert( + stream_key.clone(), + StreamInfo { + tunneled_host_opt: None, + dns_failure_retry_opt: None, + route_opt: Some(RouteQueryResponse { + route: Route { hops: vec![] }, + expected_services: ExpectedServices::RoundTrip(vec![], vec![]), + host: Host::new(&si_host.name, 2345), + }), + protocol_opt: None, + time_to_live_opt: None, + }, + ); + + let _ = subject.make_payload( + InboundClientData { + // irrelevant + timestamp: SystemTime::now(), + client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), + reception_port_opt: Some(HTTP_PORT), + last_data: false, + is_clandestine: false, + sequence_number_opt: Some(123), + data: vec![], + }, + &stream_key, + ); + + let (_ibcd, _sk, host_opt, _cryptde, _logger) = &make_params_arc.lock().unwrap()[0]; + assert_eq!(host_opt, &Some(si_host)); + } + #[test] #[should_panic( expected = "ProxyServer should never get ShutdownStreamMsg about clandestine stream" @@ -6466,10 +6643,10 @@ mod tests { let ibcd = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr, - reception_port: Some(2222), + reception_port_opt: Some(2222), last_data: true, is_clandestine: false, - sequence_number: Some(333), + sequence_number_opt: Some(333), data: b"GET /index.html HTTP/1.1\r\nHost: header.com:3333\r\n\r\n".to_vec(), }; @@ -6490,10 +6667,10 @@ mod tests { let ibcd = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr, - reception_port: Some(2222), + reception_port_opt: Some(2222), last_data: true, is_clandestine: false, - sequence_number: Some(333), + sequence_number_opt: Some(333), data: b"GET /index.html HTTP/1.1\r\nHost: header.com:4444\r\n\r\n".to_vec(), }; @@ -6506,6 +6683,30 @@ mod tests { ); } + fn make_server_com_client_hello() -> Vec { + [ + 0x16, // content_type: Handshake + 0x00, 0x00, 0x00, 0x00, // version, length: don't care + 0x01, // handshake_type: ClientHello + 0x00, 0x00, 0x00, 0x00, 0x00, // length, version: don't care + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, // random: don't care + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, // random: don't care + 0x00, // session_id_length + 0x00, 0x00, // cipher_suites_length + 0x00, // compression_methods_length + 0x00, 0x13, // extensions_length + 0x00, 0x00, // extension_type: server_name + 0x00, 0x0F, // extension_length + 0x00, 0x0D, // server_name_list_length + 0x00, // server_name_type + 0x00, 0x0A, // server_name_length + b's', b'e', b'r', b'v', b'e', b'r', b'.', b'c', b'o', b'm', // server_name + ] + .to_vec() + } + 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 9697ce6a2..e5607bb08 100644 --- a/node/src/proxy_server/protocol_pack.rs +++ b/node/src/proxy_server/protocol_pack.rs @@ -3,29 +3,16 @@ use crate::proxy_server::http_protocol_pack::HttpProtocolPack; use crate::proxy_server::tls_protocol_pack::TlsProtocolPack; use crate::sub_lib::cryptde::PlainData; use crate::sub_lib::dispatcher::InboundClientData; +use crate::sub_lib::host::Host; use crate::sub_lib::proxy_server::ProxyProtocol; use masq_lib::constants::{HTTP_PORT, TLS_PORT}; -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Host { - pub name: String, - pub port: u16, -} - -impl Host { - pub fn new(name: &str, port: u16) -> Host { - Host { - name: name.to_string(), - port, - } - } -} - pub trait ProtocolPack: Send + Sync { fn proxy_protocol(&self) -> ProxyProtocol; fn standard_port(&self) -> u16; fn find_host(&self, data: &PlainData) -> Option; fn server_impersonator(&self) -> Box; + fn describe_packet(&self, data: &PlainData) -> String; } pub fn from_protocol(protocol: ProxyProtocol) -> Box { @@ -44,7 +31,7 @@ pub fn from_standard_port(standard_port: u16) -> Option> { } pub fn from_ibcd(ibcd: &InboundClientData) -> Result, String> { - let origin_port = match ibcd.reception_port { + let origin_port = match ibcd.reception_port_opt { None => { return Err(format!( "No origin port specified with {}-byte non-clandestine packet: {:?}", @@ -67,6 +54,6 @@ pub fn from_ibcd(ibcd: &InboundClientData) -> Result, Stri pub trait ServerImpersonator { fn route_query_failure_response(&self, server_name: &str) -> Vec; - fn dns_resolution_failure_response(&self, server_name_opt: Option) -> Vec; + fn dns_resolution_failure_response(&self, server_name: String) -> Vec; fn consuming_wallet_absent(&self) -> Vec; } diff --git a/node/src/proxy_server/server_impersonator_http.rs b/node/src/proxy_server/server_impersonator_http.rs index 554741aae..9de02301d 100644 --- a/node/src/proxy_server/server_impersonator_http.rs +++ b/node/src/proxy_server/server_impersonator_http.rs @@ -19,11 +19,9 @@ impl ServerImpersonator for ServerImpersonatorHttp { ) } - fn dns_resolution_failure_response(&self, server_name_opt: Option) -> Vec { - let (server_name, quoted_server_name) = match &server_name_opt { - Some(name) => (name.clone(), format!("\"{}\"", name)), - None => ("".to_string(), "".to_string()), - }; + fn dns_resolution_failure_response(&self, server_name: String) -> Vec { + let (server_name, quoted_server_name) = + (server_name.clone(), format!("\"{}\"", server_name)); ServerImpersonatorHttp::make_error_response( 503, "DNS Resolution Problem", @@ -197,7 +195,7 @@ mod tests { fn dns_resolution_failure_response_with_server_name_produces_expected_error_page() { let subject = ServerImpersonatorHttp {}; - let result = subject.dns_resolution_failure_response(Some("server.com".to_string())); + let result = subject.dns_resolution_failure_response("server.com".to_string()); let expected = ServerImpersonatorHttp::make_error_response( 503, @@ -208,21 +206,6 @@ mod tests { assert_eq!(expected, result); } - #[test] - fn dns_resolution_failure_response_without_server_name_produces_expected_error_page() { - let subject = ServerImpersonatorHttp {}; - - let result = subject.dns_resolution_failure_response(None); - - let expected = ServerImpersonatorHttp::make_error_response( - 503, - "DNS Resolution Problem", - "Exit Nodes couldn't resolve ", - "DNS Failure, We have tried multiple Exit Nodes and all have failed to resolve this address ", - ); - assert_eq!(expected, result); - } - #[test] fn consuming_wallet_absent_response_produces_expected_error_page() { let subject = ServerImpersonatorHttp {}; diff --git a/node/src/proxy_server/server_impersonator_tls.rs b/node/src/proxy_server/server_impersonator_tls.rs index 7a193a269..3312be656 100644 --- a/node/src/proxy_server/server_impersonator_tls.rs +++ b/node/src/proxy_server/server_impersonator_tls.rs @@ -8,7 +8,7 @@ impl ServerImpersonator for ServerImpersonatorTls { Vec::from(&TLS_INTERNAL_ERROR_ALERT[..]) } - fn dns_resolution_failure_response(&self, _server_name: Option) -> Vec { + fn dns_resolution_failure_response(&self, _server_name: String) -> Vec { Vec::from(&TLS_UNRECOGNIZED_NAME_ALERT[..]) } @@ -75,7 +75,7 @@ mod tests { fn dns_resolution_failure_response_produces_unrecognized_name_alert() { let subject = ServerImpersonatorTls {}; - let result = subject.dns_resolution_failure_response(None); + let result = subject.dns_resolution_failure_response("booga.com".to_string()); assert_eq!(Vec::from(&TLS_UNRECOGNIZED_NAME_ALERT[..]), result); } diff --git a/node/src/proxy_server/tls_protocol_pack.rs b/node/src/proxy_server/tls_protocol_pack.rs index d167ef0fa..bb81f8543 100644 --- a/node/src/proxy_server/tls_protocol_pack.rs +++ b/node/src/proxy_server/tls_protocol_pack.rs @@ -1,11 +1,13 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::proxy_server::protocol_pack::{Host, ProtocolPack, ServerImpersonator}; +use crate::proxy_server::protocol_pack::{ProtocolPack, ServerImpersonator}; use crate::proxy_server::server_impersonator_tls::ServerImpersonatorTls; use crate::sub_lib::binary_traverser::BinaryTraverser; use crate::sub_lib::cryptde::PlainData; +use crate::sub_lib::host::Host; use crate::sub_lib::proxy_server::ProxyProtocol; use masq_lib::constants::TLS_PORT; +#[derive(Clone, Copy)] pub struct TlsProtocolPack {} impl ProtocolPack for TlsProtocolPack { @@ -37,6 +39,21 @@ impl ProtocolPack for TlsProtocolPack { fn server_impersonator(&self) -> Box { Box::new(ServerImpersonatorTls {}) } + + fn describe_packet(&self, data: &PlainData) -> String { + match data.get_u8(0) { + Some(0x16u8) => self.describe_handshake(data), + Some(0x14u8) => self.describe_cipher_spec(data), + Some(0x15u8) => self.describe_alert(data), + Some(0x17u8) => self.describe_application_data(data), + Some(opcode) => format!( + "{}-byte packet of unrecognized type 0x{:02X}", + data.len(), + opcode + ), + None => format!("Incomplete {}-byte TLS packet", data.len()), + } + } } impl TlsProtocolPack { @@ -99,6 +116,89 @@ impl TlsProtocolPack { Err(_) => Err(()), } } + + fn describe_handshake(&self, data: &PlainData) -> String { + match data.get_u8(5) { + Some(0x00u8) => "HelloRequest".to_string(), + Some(0x01u8) => self.describe_client_hello(data), + Some(0x02u8) => "ServerHello".to_string(), + Some(0x03u8) => "HelloVerifyRequest".to_string(), + Some(0x04u8) => "NewSessionTicket".to_string(), + Some(0x05u8) => "EndOfEarlyData".to_string(), + Some(0x06u8) => "HelloRetryRequest".to_string(), + Some(0x0Bu8) => "Certificate".to_string(), + Some(0x0Cu8) => "ServerKeyExchange".to_string(), + Some(0x0Du8) => "CertificateRequest".to_string(), + Some(0x0Eu8) => "ServerHelloDone".to_string(), + Some(0x0Fu8) => "CertificateVerify".to_string(), + Some(0x10u8) => "ClientKeyExchange".to_string(), + Some(0x14u8) => "Finished".to_string(), + Some(opcode) => format!("Unrecognized Handshake 0x{:02X}", opcode), + None => format!("Incomplete {}-byte Handshake packet", data.len()), + } + } + + fn describe_client_hello(&self, data: &PlainData) -> String { + match self.find_host(data) { + Some(host) => format!("ClientHello with SNI '{}'", host.name), + None => "ClientHello with no SNI extension".to_string(), + } + } + + fn describe_cipher_spec(&self, data: &PlainData) -> String { + match data.get_u8(5) { + Some(0x01u8) => "ChangeCipherSpec".to_string(), + Some(opcode) => format!("Unrecognized ChangeCipherSpec 0x{:02X}", opcode), + None => format!("Incomplete {}-byte ChangeCipherSpec packet", data.len()), + } + } + + fn describe_alert(&self, data: &PlainData) -> String { + let level = match data.get_u8(5) { + Some(0x01u8) => "Warning".to_string(), + Some(0x02u8) => "Fatal".to_string(), + Some(opcode) => format!("Unrecognized Alert Level 0x{:02X}", opcode), + None => return format!("Incomplete {}-byte Alert packet", data.len()), + }; + let description = match data.get_u8(6) { + Some(0x00) => "CloseNotify".to_string(), + Some(0x01) => "Unrecognized Alert Description 0x01".to_string(), + Some(0x0A) => "UnexpectedMessage".to_string(), + Some(0x14) => "BadRecordMAC".to_string(), + Some(0x15) => "DecryptionFailed".to_string(), + Some(0x16) => "RecordOverflow".to_string(), + Some(0x1E) => "DecompressionFailure".to_string(), + Some(0x28) => "HandshakeFailure".to_string(), + Some(0x29) => "NoCertificate".to_string(), + Some(0x2A) => "BadCertificate".to_string(), + Some(0x2B) => "UnsupportedCertificate".to_string(), + Some(0x2C) => "CertificateRevoked".to_string(), + Some(0x2D) => "CertificateExpired".to_string(), + Some(0x2E) => "CertificateUnknown".to_string(), + Some(0x2F) => "IllegalParameter".to_string(), + Some(0x30) => "UnknownCA".to_string(), + Some(0x31) => "AccessDenied".to_string(), + Some(0x32) => "DecodeError".to_string(), + Some(0x33) => "DecryptError".to_string(), + Some(0x3C) => "ExportRestriction".to_string(), + Some(0x46) => "ProtocolVersion".to_string(), + Some(0x47) => "InsufficientSecurity".to_string(), + Some(0x50) => "InternalError".to_string(), + Some(0x5A) => "UserCanceled".to_string(), + Some(0x64) => "NoRenegotiation".to_string(), + Some(0x72) => "UnsupportedExtension".to_string(), + Some(opcode) => format!("Unrecognized Alert Description 0x{:02X}", opcode), + None => return format!("Incomplete {}-byte Alert packet", data.len()), + }; + format!("{} {}", level, description) + } + + fn describe_application_data(&self, data: &PlainData) -> String { + if data.len() < 5 { + return "Incomplete ApplicationData".to_string(); + } + format!("{}-byte ApplicationData", data.len() - 5) + } } #[cfg(test)] @@ -796,4 +896,256 @@ mod tests { assert_eq!(None, result); } + + #[test] + fn describe_packet_handles_empty_packet() { + let data = PlainData::new(&[]); + + let result = TlsProtocolPack {}.describe_packet(&data); + + assert_eq!("Incomplete 0-byte TLS packet", result); + } + + #[test] + fn describe_packet_handles_unrecognized_packet_type() { + let data = PlainData::new(&[0xFFu8]); + + let result = TlsProtocolPack {}.describe_packet(&data); + + assert_eq!("1-byte packet of unrecognized type 0xFF", result); + } + + #[test] + fn describe_packet_handles_short_handshake_packet() { + let data = PlainData::new(&[0x16u8]); + + let result = TlsProtocolPack {}.describe_packet(&data); + + assert_eq!("Incomplete 1-byte Handshake packet", result); + } + + #[test] + fn identifies_client_hello_with_sni() { + #[rustfmt::skip] + let data = PlainData::new(&[ + 0x16, // content_type: Handshake + 0x00, 0x00, 0x00, 0x00, // version, length: don't care + 0x01, // handshake_type: ClientHello + 0x00, 0x00, 0x00, 0x00, 0x00, // length, version: don't care + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // random: don't care + 0x01, // session_id_length + 0x00, // session_id: don't care + 0x00, 0x01, // cipher_suites_length + 0x00, // cipher_suite: don't care + 0x01, // compression_methods_length + 0x00, // compression_method: don't care + 0x00, 0x13, // extensions_length + 0x00, 0x00, // extension_type: server_name + 0x00, 0x0F, // extension_length + 0x00, 0x0D, // server_name_list_length + 0x00, // server_name_type + 0x00, 0x0A, // server_name_length + b's', b'e', b'r', b'v', b'e', b'r', b'.', b'c', + b'o', b'm', // server_name + ]); + + let result = TlsProtocolPack {}.describe_packet(&data); + + assert_eq!("ClientHello with SNI 'server.com'", result); + } + + #[test] + fn identifies_client_hello_without_sni() { + #[rustfmt::skip] + let data = PlainData::new(&[ + 0x16, // content_type: Handshake + 0x00, 0x00, 0x00, 0x00, // version, length: don't care + 0x01, // handshake_type: ClientHello + 0x00, 0x00, 0x00, 0x00, 0x00, // length, version: don't care + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // random: don't care + 0x00, // session_id_length + 0x00, 0x00, // cipher_suites_length + 0x00, // compression_methods_length + 0x00, 0x00, // extensions_length + ]); + + let result = TlsProtocolPack {}.describe_packet(&data); + + assert_eq!("ClientHello with no SNI extension", result); + } + + #[test] + fn identifies_other_handshakes() { + #[rustfmt::skip] + let mut bytes: Vec = vec![ + 0x16, // content_type: Handshake + 0x00, 0x00, 0x00, 0x00, // version, length: don't care + 0x00, // handshake_type: replace me + ]; + let handshake_types = vec![ + (0x00, "HelloRequest"), + (0x02, "ServerHello"), + (0x03, "HelloVerifyRequest"), + (0x04, "NewSessionTicket"), + (0x05, "EndOfEarlyData"), + (0x06, "HelloRetryRequest"), + (0x07, "Unrecognized Handshake 0x07"), + (0x08, "Unrecognized Handshake 0x08"), + (0x0B, "Certificate"), + (0x0C, "ServerKeyExchange"), + (0x0D, "CertificateRequest"), + (0x0E, "ServerHelloDone"), + (0x0F, "CertificateVerify"), + (0x10, "ClientKeyExchange"), + (0x14, "Finished"), + ]; + handshake_types.iter().for_each(|(opcode, name)| { + bytes[5] = *opcode; + let data = PlainData::new(&bytes); + + let result = TlsProtocolPack {}.describe_packet(&data); + + assert_eq!(*name, result); + }); + } + + #[test] + fn identifies_cipher_spec_packets() { + #[rustfmt::skip] + let mut bytes: Vec = vec![ + 0x14, // content_type: ChangeCipherSpec + 0x00, 0x00, 0x00, 0x00, // version, length: don't care + 0x00, // change_cipher_spec: replace me + ]; + let change_cipher_specs = vec![ + (0x00, "Unrecognized ChangeCipherSpec 0x00"), + (0x01, "ChangeCipherSpec"), + (0x02, "Unrecognized ChangeCipherSpec 0x02"), + ]; + change_cipher_specs.iter().for_each(|(opcode, name)| { + bytes[5] = *opcode; + let data = PlainData::new(&bytes); + + let result = TlsProtocolPack {}.describe_packet(&data); + + assert_eq!(*name, result); + }); + } + + #[test] + fn handles_incomplete_cipher_spec_packet() { + #[rustfmt::skip] + let data = PlainData::new(&[ + 0x14, // content_type: ChangeCipherSpec + ]); + + let result = TlsProtocolPack {}.describe_packet(&data); + + assert_eq!("Incomplete 1-byte ChangeCipherSpec packet", result); + } + + #[test] + fn identifies_alert_packets() { + #[rustfmt::skip] + let mut bytes: Vec = vec![ + 0x15, // content_type: Alert + 0x00, 0x00, 0x00, 0x00, // version, length: don't care + 0x00, // alert_level: replace me + 0x00, // alert_description: replace me + ]; + let alert_levels = vec![ + (0x01, "Warning"), + (0x02, "Fatal"), + (0x03, "Unrecognized Alert Level 0x03"), + (0x04, "Unrecognized Alert Level 0x04"), + ]; + let alert_descriptions = vec![ + (0x00, "CloseNotify"), + (0x01, "Unrecognized Alert Description 0x01"), + (0x0A, "UnexpectedMessage"), + (0x14, "BadRecordMAC"), + (0x15, "DecryptionFailed"), + (0x16, "RecordOverflow"), + (0x1E, "DecompressionFailure"), + (0x28, "HandshakeFailure"), + (0x29, "NoCertificate"), + (0x2A, "BadCertificate"), + (0x2B, "UnsupportedCertificate"), + (0x2C, "CertificateRevoked"), + (0x2D, "CertificateExpired"), + (0x2E, "CertificateUnknown"), + (0x2F, "IllegalParameter"), + (0x30, "UnknownCA"), + (0x31, "AccessDenied"), + (0x32, "DecodeError"), + (0x33, "DecryptError"), + (0x3C, "ExportRestriction"), + (0x46, "ProtocolVersion"), + (0x47, "InsufficientSecurity"), + (0x50, "InternalError"), + (0x5A, "UserCanceled"), + (0x64, "NoRenegotiation"), + (0x72, "UnsupportedExtension"), + (0xFF, "Unrecognized Alert Description 0xFF"), + ]; + alert_descriptions + .iter() + .for_each(|(description, description_name)| { + bytes[6] = *description; + alert_levels.iter().for_each(|(level, level_name)| { + bytes[5] = *level; + let data = PlainData::new(&bytes); + + let result = TlsProtocolPack {}.describe_packet(&data); + + let expected = format!("{} {}", level_name, description_name); + assert_eq!(expected, result); + }); + }); + } + + #[test] + fn handles_incomplete_alert_packet() { + #[rustfmt::skip] + let data = PlainData::new(&[ + 0x15, // content_type: Alert + ]); + + let result = TlsProtocolPack {}.describe_packet(&data); + + assert_eq!("Incomplete 1-byte Alert packet", result); + } + + #[test] + fn identifies_application_data_packets() { + #[rustfmt::skip] + let data = PlainData::new(&[ + 0x17, // content_type: ApplicationData + 0x00, 0x00, 0x00, 0x00, // version, length: don't care + 0x01, 0x02, 0x03, 0x04, 0x05, // data + ]); + + let result = TlsProtocolPack {}.describe_packet(&data); + + assert_eq!("5-byte ApplicationData", result); + } + + #[test] + fn handles_short_application_data_packets() { + #[rustfmt::skip] + let data = PlainData::new(&[ + 0x17, // content_type: ApplicationData + 0x00, 0x00, 0x00, // Too short by one byte + ]); + + let result = TlsProtocolPack {}.describe_packet(&data); + + assert_eq!("Incomplete ApplicationData", result); + } } diff --git a/node/src/stream_handler_pool.rs b/node/src/stream_handler_pool.rs index 7aa6d0ff6..b759a703c 100644 --- a/node/src/stream_handler_pool.rs +++ b/node/src/stream_handler_pool.rs @@ -516,7 +516,7 @@ impl StreamHandlerPool { sw_key ); debug!(self.logger, "Masking {} bytes", msg.context.data.len()); - let packet = if msg.context.sequence_number.is_none() { + let packet = if msg.context.sequence_number_opt.is_none() { let masquerader = self.traffic_analyzer.get_masquerader(); match masquerader.mask(msg.context.data.as_slice()) { Ok(masked_data) => SequencedPacket::new(masked_data, 0, false), @@ -849,7 +849,7 @@ mod tests { let peer_addr = SocketAddr::from_str("1.2.3.4:80").unwrap(); let peer_addr_a = peer_addr.clone(); let local_addr = SocketAddr::from_str("1.2.3.5:80").unwrap(); - let reception_port = Some(8081); + let reception_port_opt = Some(8081); let is_clandestine = false; let one_http_req = b"GET http://here.com HTTP/1.1\r\n\r\n".to_vec(); let one_http_req_a = one_http_req.clone(); @@ -901,7 +901,7 @@ mod tests { .add_sub .try_send(AddStreamMsg::new( connection_info, // the stream splitter mock will return mocked reader/writer - reception_port, + reception_port_opt, PortConfiguration::new( vec![Box::new(HttpRequestDiscriminatorFactory::new())], is_clandestine, @@ -922,10 +922,10 @@ mod tests { &dispatcher::InboundClientData { timestamp: dispatcher_record.timestamp, client_addr: peer_addr_a, - reception_port, + reception_port_opt, last_data: false, is_clandestine, - sequence_number: Some(0), + sequence_number_opt: Some(0), data: one_http_req_a, } ); @@ -936,10 +936,10 @@ mod tests { &dispatcher::InboundClientData { timestamp: dispatcher_record.timestamp, client_addr: peer_addr_a, - reception_port, + reception_port_opt, last_data: false, is_clandestine, - sequence_number: Some(1), + sequence_number_opt: Some(1), data: another_http_req_a, } ); @@ -950,10 +950,10 @@ mod tests { &dispatcher::InboundClientData { timestamp: dispatcher_record.timestamp, client_addr: peer_addr_a, - reception_port, + reception_port_opt, last_data: false, is_clandestine, - sequence_number: Some(2), + sequence_number_opt: Some(2), data: a_third_http_req_a, } ); @@ -963,7 +963,7 @@ mod tests { &dispatcher::StreamShutdownMsg { peer_addr: peer_addr_a, stream_type: RemovedStreamType::NonClandestine(NonClandestineAttributes { - reception_port: reception_port.unwrap(), + reception_port: reception_port_opt.unwrap(), sequence_number: 3 }), report_to_counterpart: true, @@ -1025,7 +1025,7 @@ mod tests { .try_send(TransmitDataMsg { endpoint: Endpoint::Socket(peer_addr), last_data: true, - sequence_number: Some(0), + sequence_number_opt: Some(0), data: b"hello".to_vec(), }) .unwrap(); @@ -1107,7 +1107,7 @@ mod tests { .try_send(TransmitDataMsg { endpoint: Endpoint::Socket(peer_addr), last_data: true, - sequence_number: Some(0), + sequence_number_opt: Some(0), data: vec![0x12, 0x34], }) .unwrap(); @@ -1130,7 +1130,7 @@ mod tests { .try_send(TransmitDataMsg { endpoint: Endpoint::Socket(peer_addr), last_data: true, - sequence_number: Some(0), + sequence_number_opt: Some(0), data: vec![0x56, 0x78], }) .unwrap(); @@ -1209,7 +1209,7 @@ mod tests { .try_send(TransmitDataMsg { endpoint: Endpoint::Socket(peer_addr), last_data: true, - sequence_number: Some(0), + sequence_number_opt: Some(0), data: vec![0x12, 0x34], }) .unwrap(); @@ -1381,7 +1381,7 @@ mod tests { context: TransmitDataMsg { endpoint: Endpoint::Key(public_key), last_data: false, - sequence_number: None, + sequence_number_opt: None, data: b"hello".to_vec(), }, }) @@ -1470,7 +1470,7 @@ mod tests { .try_send(TransmitDataMsg { endpoint: Endpoint::Key(public_key.clone()), last_data: false, - sequence_number: None, + sequence_number_opt: None, data: outgoing_unmasked, }) .unwrap(); @@ -1505,10 +1505,10 @@ mod tests { &InboundClientData { timestamp: ibcd.timestamp, client_addr: SocketAddr::from_str("1.2.3.5:7000").unwrap(), - reception_port: Some(54321), + reception_port_opt: Some(54321), last_data: false, is_clandestine: true, - sequence_number: None, + sequence_number_opt: None, data: incoming_unmasked, } ); @@ -1586,7 +1586,7 @@ mod tests { .try_send(TransmitDataMsg { endpoint: Endpoint::Key(key.clone()), last_data: false, - sequence_number: Some(0), + sequence_number_opt: Some(0), data: b"hello".to_vec(), }) .unwrap(); @@ -1646,7 +1646,7 @@ mod tests { context: TransmitDataMsg { endpoint: Endpoint::Key(key.clone()), last_data: false, - sequence_number: Some(0), + sequence_number_opt: Some(0), data: b"hello".to_vec(), }, }) @@ -1698,7 +1698,7 @@ mod tests { context: TransmitDataMsg { endpoint: Endpoint::Key(key.clone()), last_data: false, - sequence_number: None, + sequence_number_opt: None, data: b"hello".to_vec(), }, }) @@ -1728,7 +1728,7 @@ mod tests { let msg = TransmitDataMsg { endpoint: Endpoint::Socket(peer_addr.clone()), last_data: false, - sequence_number: Some(0), + sequence_number_opt: Some(0), data: b"hello".to_vec(), }; let msg_a = msg.clone(); @@ -1840,7 +1840,7 @@ mod tests { context: TransmitDataMsg { endpoint: Endpoint::Socket(peer_addr.clone()), last_data: true, - sequence_number: Some(0), + sequence_number_opt: Some(0), data: b"hello".to_vec(), }, }; @@ -1869,13 +1869,13 @@ mod tests { let msg = TransmitDataMsg { endpoint: Endpoint::Socket(peer_addr.clone()), last_data: false, - sequence_number: None, + sequence_number_opt: None, data: b"hello".to_vec(), }; let msg_a = TransmitDataMsg { endpoint: Endpoint::Socket(peer_addr.clone()), last_data: false, - sequence_number: None, + sequence_number_opt: None, data: b"worlds".to_vec(), }; let expected_data = JsonMasquerader::new().mask(&msg_a.data).unwrap(); @@ -1995,7 +1995,7 @@ mod tests { context: TransmitDataMsg { endpoint: Endpoint::Socket(peer_addr.clone()), last_data: true, - sequence_number: Some(0), + sequence_number_opt: Some(0), data: b"hello".to_vec(), }, }); @@ -2035,7 +2035,7 @@ mod tests { let msg = TransmitDataMsg { endpoint: Endpoint::Socket(peer_addr.clone()), last_data: false, - sequence_number: None, + sequence_number_opt: None, data: b"hello".to_vec(), }; @@ -2126,7 +2126,7 @@ mod tests { .try_send(TransmitDataMsg { endpoint: Endpoint::Socket(peer_addr), last_data: false, - sequence_number: None, + sequence_number_opt: None, data: hello, }) .unwrap(); @@ -2136,7 +2136,7 @@ mod tests { .try_send(TransmitDataMsg { endpoint: Endpoint::Socket(peer_addr), last_data: false, - sequence_number: None, + sequence_number_opt: None, data: worlds, }) .unwrap(); @@ -2200,7 +2200,7 @@ mod tests { .try_send(TransmitDataMsg { endpoint: Endpoint::Socket(peer_addr), last_data: false, - sequence_number: None, + sequence_number_opt: None, data: b"hello".to_vec(), }) .unwrap(); @@ -2249,7 +2249,7 @@ mod tests { .try_send(TransmitDataMsg { endpoint: Endpoint::Socket(local_addr), last_data: false, - sequence_number: Some(0), + sequence_number_opt: Some(0), data: outgoing_unmasked, }) .unwrap(); diff --git a/node/src/stream_reader.rs b/node/src/stream_reader.rs index cac9211a6..df8dd8f33 100644 --- a/node/src/stream_reader.rs +++ b/node/src/stream_reader.rs @@ -19,7 +19,7 @@ pub struct StreamReaderReal { stream: Box, local_addr: SocketAddr, peer_addr: SocketAddr, - reception_port: Option, + reception_port_opt: Option, ibcd_sub: Recipient, remove_sub: Recipient, dispatcher_stream_shutdown_sub: Recipient, @@ -86,7 +86,7 @@ impl StreamReaderReal { #[allow(clippy::too_many_arguments)] pub fn new( stream: Box, - reception_port: Option, + reception_port_opt: Option, ibcd_sub: Recipient, remove_sub: Recipient, dispatcher_sub: Recipient, @@ -107,7 +107,7 @@ impl StreamReaderReal { stream, local_addr, peer_addr, - reception_port, + reception_port_opt, ibcd_sub, remove_sub, dispatcher_stream_shutdown_sub: dispatcher_sub, @@ -137,7 +137,7 @@ impl StreamReaderReal { // handshake and should start the sequence at Some(0) as well, the ProxyServer will // handle the sequenced packet offset before sending them through the stream_writer // and avoid dropping duplicate packets. - let sequence_number = if unmasked_chunk.sequenced && !is_connect { + let sequence_number_opt = if unmasked_chunk.sequenced && !is_connect { Some(self.sequencer.next_sequence_number()) } else if is_connect { // This case needs to explicitly be Some(0) instead of None so that the StreamHandlerPool does @@ -146,7 +146,7 @@ impl StreamReaderReal { } else { None }; - match sequence_number { + match sequence_number_opt { Some(num) => debug!( self.logger, "Read {} bytes of clear data (#{})", @@ -162,10 +162,10 @@ impl StreamReaderReal { let msg = dispatcher::InboundClientData { timestamp: SystemTime::now(), client_addr: self.peer_addr, - reception_port: self.reception_port, + reception_port_opt: self.reception_port_opt, last_data: false, is_clandestine: self.is_clandestine, - sequence_number, + sequence_number_opt, data: unmasked_chunk.chunk.clone(), }; debug!(self.logger, "Discriminator framed and unmasked {} bytes for {}; transmitting via Hopper", @@ -181,7 +181,7 @@ impl StreamReaderReal { } fn shutdown(&mut self) { - debug!(self.logger, "Directing removal of {}clandestine StreamReader with reception_port {:?} on {} listening to {}", if self.is_clandestine {""} else {"non-"}, self.reception_port, self.local_addr, self.peer_addr); + debug!(self.logger, "Directing removal of {}clandestine StreamReader with reception_port {:?} on {} listening to {}", if self.is_clandestine {""} else {"non-"}, self.reception_port_opt, self.local_addr, self.peer_addr); self.remove_sub .try_send(RemoveStreamMsg { peer_addr: self.peer_addr, @@ -190,7 +190,7 @@ impl StreamReaderReal { RemovedStreamType::Clandestine } else { RemovedStreamType::NonClandestine(NonClandestineAttributes { - reception_port: self.reception_port.expect( + reception_port: self.reception_port_opt.expect( "Non-clandestine StreamReader should always have a reception_port", ), sequence_number: self.sequencer.next_sequence_number(), @@ -511,10 +511,10 @@ mod tests { &dispatcher::InboundClientData { timestamp: d_record.timestamp, client_addr: peer_addr, - reception_port: Some(1234 as u16), + reception_port_opt: Some(1234 as u16), last_data: false, is_clandestine: true, - sequence_number: Some(0), + sequence_number_opt: Some(0), data: Vec::from("GET http://here.com HTTP/1.1\r\n\r\n".as_bytes()), } ); @@ -575,13 +575,13 @@ mod tests { Some(0), d_recording .get_record::(0) - .sequence_number, + .sequence_number_opt, ); assert_eq!( Some(0), d_recording .get_record::(1) - .sequence_number, + .sequence_number_opt, ); } @@ -633,10 +633,10 @@ mod tests { &dispatcher::InboundClientData { timestamp: d_record.timestamp, client_addr: peer_addr, - reception_port: Some(1234 as u16), + reception_port_opt: Some(1234 as u16), last_data: false, is_clandestine: false, - sequence_number: Some(0), + sequence_number_opt: Some(0), data: Vec::from("GET http://here.com HTTP/1.1\r\n\r\n".as_bytes()), } ); @@ -648,10 +648,10 @@ mod tests { &dispatcher::InboundClientData { timestamp: d_record.timestamp, client_addr: peer_addr, - reception_port: Some(1234 as u16), + reception_port_opt: Some(1234 as u16), last_data: false, is_clandestine: false, - sequence_number: Some(1), + sequence_number_opt: Some(1), data: Vec::from("GET http://www.example.com HTTP/1.1\r\n\r\n".as_bytes()), } ); @@ -707,10 +707,10 @@ mod tests { &dispatcher::InboundClientData { timestamp: d_record.timestamp, client_addr, - reception_port: Some(1234 as u16), + reception_port_opt: Some(1234 as u16), last_data: false, is_clandestine: true, - sequence_number: None, + sequence_number_opt: None, data: Vec::from("GET http://here.com HTTP/1.1\r\n\r\n".as_bytes()), } ); diff --git a/node/src/sub_lib/dispatcher.rs b/node/src/sub_lib/dispatcher.rs index 66e349e11..e04401220 100644 --- a/node/src/sub_lib/dispatcher.rs +++ b/node/src/sub_lib/dispatcher.rs @@ -116,10 +116,10 @@ pub enum DispatcherError { pub struct InboundClientData { pub timestamp: SystemTime, pub client_addr: SocketAddr, - pub reception_port: Option, + pub reception_port_opt: Option, pub last_data: bool, pub is_clandestine: bool, - pub sequence_number: Option, + pub sequence_number_opt: Option, pub data: Vec, } @@ -130,7 +130,7 @@ impl Debug for InboundClientData { Err(_) => self.data.hex_dump().to_string(), }; write!(f, "InboundClientData {{ peer_addr: {:?}, reception_port: {:?}, last_data: {}, sequence_number: {:?}, {} bytes of data: {} }}", - self.client_addr, self.reception_port, self.last_data, self.sequence_number, self.data.len(), data_string) + self.client_addr, self.reception_port_opt, self.last_data, self.sequence_number_opt, self.data.len(), data_string) } } @@ -139,10 +139,10 @@ impl InboundClientData { InboundClientData { timestamp: SystemTime::now(), client_addr: self.client_addr, - reception_port: self.reception_port, + reception_port_opt: self.reception_port_opt, last_data: self.last_data, is_clandestine: self.is_clandestine, - sequence_number: self.sequence_number, + sequence_number_opt: self.sequence_number_opt, data: vec![], } } @@ -274,10 +274,10 @@ mod tests { let subject = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.4.3.2:9999").unwrap(), - reception_port: None, + reception_port_opt: None, last_data: false, is_clandestine: false, - sequence_number: None, + sequence_number_opt: None, data: b"CONNECT server.example.com:80 HTTP/1.1\r\nHost: server.example.com:80\r\nProxy-Authorization: basic aGVsbG86d29ybGQ=\r\n\r\n".to_vec(), }; @@ -289,10 +289,10 @@ mod tests { let subject = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.4.3.2:9999").unwrap(), - reception_port: None, + reception_port_opt: None, last_data: false, is_clandestine: false, - sequence_number: None, + sequence_number_opt: None, data: b"GET server.example.com:80 HTTP/1.1\r\nHost: server.example.com:80\r\nProxy-Authorization: basic aGVsbG86d29ybGQ=\r\n\r\n".to_vec(), }; @@ -304,10 +304,10 @@ mod tests { let subject = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.4.3.2:9999").unwrap(), - reception_port: None, + reception_port_opt: None, last_data: false, is_clandestine: false, - sequence_number: None, + sequence_number_opt: None, data: b"CONNECTX".to_vec(), }; diff --git a/node/src/sub_lib/hopper.rs b/node/src/sub_lib/hopper.rs index 96d756ef2..08af13bb5 100644 --- a/node/src/sub_lib/hopper.rs +++ b/node/src/sub_lib/hopper.rs @@ -173,6 +173,7 @@ mod tests { use crate::sub_lib::cryptde::PlainData; use crate::sub_lib::dispatcher::Component; use crate::sub_lib::route::RouteSegment; + use crate::sub_lib::stream_key::StreamKey; use crate::test_utils::recorder::Recorder; use crate::test_utils::{make_meaningless_message_type, make_paying_wallet}; use actix::Actor; @@ -205,7 +206,7 @@ mod tests { let cryptde = CRYPTDE_PAIR.main.as_ref(); let public_key = PublicKey::new(&[1, 2]); let node_addr = NodeAddr::new(&IpAddr::from_str("1.2.3.4").unwrap(), &[1, 2, 3, 4]); - let payload = make_meaningless_message_type(); + let payload = make_meaningless_message_type(StreamKey::make_meaningless_stream_key()); let result = NoLookupIncipientCoresPackage::new(cryptde, &public_key, &node_addr, payload.clone()); @@ -231,7 +232,7 @@ mod tests { cryptde, &PublicKey::new(&[]), &NodeAddr::new(&IpAddr::from_str("1.1.1.1").unwrap(), &[]), - make_meaningless_message_type(), + make_meaningless_message_type(StreamKey::make_meaningless_stream_key()), ); assert_eq!( result, @@ -255,7 +256,7 @@ mod tests { Some(TEST_DEFAULT_CHAIN.rec().contract), ) .unwrap(); - let payload = make_meaningless_message_type(); + let payload = make_meaningless_message_type(StreamKey::make_meaningless_stream_key()); let result = IncipientCoresPackage::new(cryptde, route.clone(), payload.clone(), &key56); let subject = result.unwrap(); @@ -278,7 +279,7 @@ mod tests { let result = IncipientCoresPackage::new( cryptde, Route { hops: vec![] }, - make_meaningless_message_type(), + make_meaningless_message_type(StreamKey::make_meaningless_stream_key()), &PublicKey::new(&[]), ); @@ -304,7 +305,7 @@ mod tests { Some(TEST_DEFAULT_CHAIN.rec().contract), ) .unwrap(); - let payload = make_meaningless_message_type(); + let payload = make_meaningless_message_type(StreamKey::make_meaningless_stream_key()); let subject: ExpiredCoresPackage = ExpiredCoresPackage::new( immediate_neighbor, diff --git a/node/src/sub_lib/host.rs b/node/src/sub_lib/host.rs new file mode 100644 index 000000000..bb23d8d16 --- /dev/null +++ b/node/src/sub_lib/host.rs @@ -0,0 +1,39 @@ +use std::fmt::Display; + +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Host { + pub name: String, + pub port: u16, +} + +impl Display for Host { + fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { + write!(f, "{}:{}", self.name, self.port) + } +} + +impl Host { + pub fn new(name: &str, port: u16) -> Host { + Host { + name: name.to_string(), + port, + } + } +} + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn display() { + let subject = Host { + name: "example.com".to_string(), + port: 8080, + }; + + let result = format!("{}", subject); + + assert_eq!(result, "example.com:8080".to_string()); + } +} diff --git a/node/src/sub_lib/http_packet_framer.rs b/node/src/sub_lib/http_packet_framer.rs index 29685f46b..28d4f895f 100644 --- a/node/src/sub_lib/http_packet_framer.rs +++ b/node/src/sub_lib/http_packet_framer.rs @@ -104,7 +104,7 @@ impl HttpPacketFramer { lines: Vec::new(), }, start_finder, - logger: Logger::new("HttpRequestFramer"), + logger: Logger::new("HttpPacketFramer"), } } diff --git a/node/src/sub_lib/migrations/client_request_payload.rs b/node/src/sub_lib/migrations/client_request_payload.rs index 2bd9993f5..b048c1900 100644 --- a/node/src/sub_lib/migrations/client_request_payload.rs +++ b/node/src/sub_lib/migrations/client_request_payload.rs @@ -49,7 +49,7 @@ impl TryFrom<&Value> for ClientRequestPayload_0v1 { Value::Map(map) => { let mut stream_key_opt: Option = None; let mut sequenced_packet_opt: Option = None; - let mut target_hostname_opt: Option> = None; + let mut target_hostname: Option = None; let mut target_port_opt: Option = None; let mut protocol_opt: Option = None; let mut originator_public_key_opt: Option = None; @@ -61,9 +61,7 @@ impl TryFrom<&Value> for ClientRequestPayload_0v1 { "sequenced_packet" => { sequenced_packet_opt = value_to_type::(v) } - "target_hostname" => { - target_hostname_opt = value_to_type::>(v) - } + "target_hostname" => target_hostname = value_to_type::(v), "target_port" => target_port_opt = value_to_type::(v), "protocol" => protocol_opt = value_to_type::(v), "originator_public_key" => { @@ -89,7 +87,7 @@ impl TryFrom<&Value> for ClientRequestPayload_0v1 { "sequenced_packet", &sequenced_packet_opt, ); - check_field(&mut missing_fields, "target_hostname", &target_hostname_opt); + check_field(&mut missing_fields, "target_hostname", &target_hostname); check_field(&mut missing_fields, "target_port", &target_port_opt); check_field(&mut missing_fields, "protocol", &protocol_opt); check_field( @@ -103,7 +101,7 @@ impl TryFrom<&Value> for ClientRequestPayload_0v1 { Ok(ClientRequestPayload_0v1 { stream_key: stream_key_opt.expect("stream_key disappeared"), sequenced_packet: sequenced_packet_opt.expect("sequenced_packet disappeared"), - target_hostname: target_hostname_opt.expect("target_hostname disappeared"), + target_hostname: target_hostname.expect("target_hostname disappeared"), target_port: target_port_opt.expect("target_port disappeared"), protocol: protocol_opt.expect("protocol disappeared"), originator_public_key: originator_public_key_opt @@ -131,7 +129,7 @@ mod tests { struct ExampleFutureCRP { pub stream_key: StreamKey, pub sequenced_packet: SequencedPacket, - pub target_hostname: Option, + pub target_hostname: String, pub target_port: u16, pub protocol: ProxyProtocol, pub originator_public_key: PublicKey, @@ -141,7 +139,7 @@ mod tests { let expected_crp = ClientRequestPayload_0v1 { stream_key: StreamKey::make_meaningful_stream_key("All Things Must Pass"), sequenced_packet: SequencedPacket::new(vec![4, 3, 2, 1], 4321, false), - target_hostname: Some("target.hostname.com".to_string()), + target_hostname: "target.hostname.com".to_string(), target_port: 1234, protocol: ProxyProtocol::HTTP, originator_public_key: PublicKey::new(&[2, 3, 4, 5]), diff --git a/node/src/sub_lib/mod.rs b/node/src/sub_lib/mod.rs index 51360357b..4c1a76fce 100644 --- a/node/src/sub_lib/mod.rs +++ b/node/src/sub_lib/mod.rs @@ -21,6 +21,7 @@ pub mod framer; pub mod framer_utils; pub mod hop; pub mod hopper; +pub mod host; pub mod http_packet_framer; pub mod http_response_start_finder; pub mod limiter; diff --git a/node/src/sub_lib/neighborhood.rs b/node/src/sub_lib/neighborhood.rs index f26282aa6..674f83191 100644 --- a/node/src/sub_lib/neighborhood.rs +++ b/node/src/sub_lib/neighborhood.rs @@ -8,6 +8,7 @@ use crate::sub_lib::cryptde::{CryptDE, PublicKey}; use crate::sub_lib::cryptde_real::CryptDEReal; use crate::sub_lib::dispatcher::{Component, StreamShutdownMsg}; use crate::sub_lib::hopper::ExpiredCoresPackage; +use crate::sub_lib::host::Host; use crate::sub_lib::node_addr::NodeAddr; use crate::sub_lib::peer_actors::{BindMessage, NewPublicIp, StartMessage}; use crate::sub_lib::route::Route; @@ -473,7 +474,7 @@ pub struct RouteQueryMessage { pub target_component: Component, pub return_component_opt: Option, pub payload_size: usize, - pub hostname_opt: Option, + pub host: Host, } impl Message for RouteQueryMessage { @@ -481,16 +482,13 @@ impl Message for RouteQueryMessage { } impl RouteQueryMessage { - pub fn data_indefinite_route_request( - hostname_opt: Option, - payload_size: usize, - ) -> RouteQueryMessage { + pub fn data_indefinite_route_request(host: Host, payload_size: usize) -> RouteQueryMessage { RouteQueryMessage { target_key_opt: None, target_component: Component::ProxyClient, return_component_opt: Some(Component::ProxyServer), payload_size, - hostname_opt, + host, } } } @@ -502,16 +500,53 @@ pub enum ExpectedService { Nothing, } +impl ExpectedService { + pub fn exit_node_key_opt(&self) -> Option { + match self { + ExpectedService::Exit(key, _, _) => Some(key.clone()), + _ => None, + } + } + + pub fn public_key_opt(&self) -> Option { + match self { + ExpectedService::Exit(key, _, _) | ExpectedService::Routing(key, _, _) => { + Some(key.clone()) + } + _ => None, + } + } + + pub fn wallet_opt(&self) -> Option<&Wallet> { + match self { + ExpectedService::Exit(_, wallet, _) | ExpectedService::Routing(_, wallet, _) => { + Some(wallet) + } + _ => None, + } + } + + pub fn rate_pack_opt(&self) -> Option<&RatePack> { + match self { + ExpectedService::Exit(_, _, rate_pack) | ExpectedService::Routing(_, _, rate_pack) => { + Some(rate_pack) + } + _ => None, + } + } +} + #[derive(Clone, Debug, PartialEq, Eq)] pub enum ExpectedServices { OneWay(Vec), - RoundTrip(Vec, Vec, u32), + RoundTrip(Vec, Vec), } #[derive(Clone, Debug, PartialEq, Eq)] pub struct RouteQueryResponse { pub route: Route, pub expected_services: ExpectedServices, + pub host: Host, } #[derive(Clone, Debug, Message, PartialEq, Eq)] @@ -1060,7 +1095,8 @@ mod tests { #[test] fn data_indefinite_route_request() { - let result = RouteQueryMessage::data_indefinite_route_request(None, 7500); + let result = + RouteQueryMessage::data_indefinite_route_request(Host::new("booga.com", 1234), 7500); assert_eq!( result, @@ -1069,7 +1105,7 @@ mod tests { target_component: Component::ProxyClient, return_component_opt: Some(Component::ProxyServer), payload_size: 7500, - hostname_opt: None, + host: Host::new("booga.com", 1234), } ); } diff --git a/node/src/sub_lib/proxy_server.rs b/node/src/sub_lib/proxy_server.rs index c3042859f..c7b0ab49c 100644 --- a/node/src/sub_lib/proxy_server.rs +++ b/node/src/sub_lib/proxy_server.rs @@ -4,7 +4,7 @@ use crate::sub_lib::data_version::DataVersion; use crate::sub_lib::dispatcher::InboundClientData; use crate::sub_lib::dispatcher::StreamShutdownMsg; use crate::sub_lib::hopper::{ExpiredCoresPackage, MessageType}; -use crate::sub_lib::neighborhood::{ExpectedService, RouteQueryResponse}; +use crate::sub_lib::neighborhood::RouteQueryResponse; use crate::sub_lib::peer_actors::BindMessage; use crate::sub_lib::proxy_client::{ClientResponsePayload_0v1, DnsResolveFailure_0v1}; use crate::sub_lib::sequence_buffer::SequencedPacket; @@ -34,7 +34,7 @@ pub enum ProxyProtocol { pub struct ClientRequestPayload_0v1 { pub stream_key: StreamKey, pub sequenced_packet: SequencedPacket, - pub target_hostname: Option, + pub target_hostname: String, pub target_port: u16, pub protocol: ProxyProtocol, pub originator_public_key: PublicKey, @@ -55,14 +55,6 @@ impl ClientRequestPayload_0v1 { } } -#[derive(Message, Debug, PartialEq, Eq)] -pub struct AddReturnRouteMessage { - pub return_route_id: u32, - pub expected_services: Vec, - pub protocol: ProxyProtocol, - pub hostname_opt: Option, -} - #[derive(Message, Debug, PartialEq, Eq)] pub struct AddRouteResultMessage { pub stream_key: StreamKey, @@ -81,7 +73,6 @@ pub struct ProxyServerSubs { pub from_dispatcher: Recipient, pub from_hopper: Recipient>, pub dns_failure_from_hopper: Recipient>, - pub add_return_route: Recipient, pub stream_shutdown_sub: Recipient, pub node_from_ui: Recipient, pub route_result_sub: Recipient, @@ -113,7 +104,6 @@ mod tests { recorder, ExpiredCoresPackage ), - add_return_route: recipient!(recorder, AddReturnRouteMessage), stream_shutdown_sub: recipient!(recorder, StreamShutdownMsg), node_from_ui: recipient!(recorder, NodeFromUiMessage), route_result_sub: recipient!(recorder, AddRouteResultMessage), diff --git a/node/src/sub_lib/route.rs b/node/src/sub_lib/route.rs index 6d9b52604..05bcc15ad 100644 --- a/node/src/sub_lib/route.rs +++ b/node/src/sub_lib/route.rs @@ -1,5 +1,4 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::sub_lib::cryptde::encodex; use crate::sub_lib::cryptde::CryptDE; use crate::sub_lib::cryptde::CryptData; use crate::sub_lib::cryptde::PublicKey; @@ -33,7 +32,6 @@ impl Route { cryptde, None, None, - None, ) } @@ -48,7 +46,6 @@ impl Route { None, cryptde, consuming_wallet, - None, contract_address, ) } @@ -56,9 +53,8 @@ impl Route { pub fn round_trip( route_segment_over: RouteSegment, route_segment_back: RouteSegment, - cryptde: &dyn CryptDE, // Must be the CryptDE of the originating Node: used to encrypt return_route_id. + cryptde: &dyn CryptDE, // Doesn't matter which CryptDE: only used for encoding. consuming_wallet: Option, - return_route_id: u32, contract_address: Option
, ) -> Result { Self::construct( @@ -66,22 +62,10 @@ impl Route { Some(route_segment_back), cryptde, consuming_wallet, - Some(return_route_id), contract_address, ) } - pub fn id(&self, cryptde: &dyn CryptDE) -> Result { - if let Some(first) = self.hops.first() { - match decodex(cryptde, first) { - Ok(n) => Ok(n), - Err(e) => Err(format!("{:?}", e)), - } - } else { - Err("Response route did not contain a return route ID".to_string()) - } - } - // This cryptde must be the CryptDE of the next hop to come off the Route. pub fn next_hop(&self, cryptde: &dyn CryptDE) -> Result { match self.hops.first() { @@ -132,15 +116,7 @@ impl Route { last_cryptde.public_key(), live_hop ), - Err(outside) => match decodex::(last_cryptde, &last_hop_enc) { - Ok(return_route_id) => format!( - "{}\nEncrypted with {:?}: Return Route ID: {}\n", - most_strings, - last_cryptde.public_key(), - return_route_id - ), - Err(inside) => format!("{}\nError: {:?} / {:?}", most_strings, outside, inside), - }, + Err(error) => format!("{}\nError: {:?}", most_strings, error), } } @@ -149,7 +125,6 @@ impl Route { back: Option, cryptde: &dyn CryptDE, consuming_wallet: Option, - return_route_id_opt: Option, contract_address: Option
, ) -> Result { if let Some(error) = Route::validate_route_segments(&over, &back) { @@ -174,12 +149,7 @@ impl Route { contract_address, ); - Route::hops_to_route( - hops[0..].to_vec(), - &over.keys[0], - return_route_id_opt, - cryptde, - ) + Route::hops_to_route(hops[0..].to_vec(), &over.keys[0], cryptde) } fn over_segment<'a>( @@ -296,7 +266,6 @@ impl Route { fn hops_to_route( hops: Vec, top_hop_key: &PublicKey, - return_route_id_opt: Option, cryptde: &dyn CryptDE, ) -> Result { let mut hops_enc: Vec = Vec::new(); @@ -308,17 +277,8 @@ impl Route { }); hop_key = &data_hop.public_key; } - if let Some(return_route_id) = return_route_id_opt { - let return_route_id_enc = Self::encrypt_return_route_id(return_route_id, cryptde); - hops_enc.push(return_route_id_enc); - } Ok(Route { hops: hops_enc }) } - - fn encrypt_return_route_id(return_route_id: u32, cryptde: &dyn CryptDE) -> CryptData { - encodex(cryptde, cryptde.public_key(), &return_route_id) - .expect("Internal error encrypting u32 return_route_id") - } } pub struct RouteSegment { @@ -365,66 +325,6 @@ mod tests { static ref CRYPTDE_PAIR: CryptDEPair = CryptDEPair::null(); } - #[test] - fn id_decodes_return_route_id() { - let cryptde = CRYPTDE_PAIR.main.as_ref(); - - let subject = Route { - hops: vec![Route::encrypt_return_route_id(42, cryptde)], - }; - - assert_eq!(subject.id(cryptde), Ok(42)); - } - - #[test] - fn id_returns_empty_route_error_when_the_route_is_empty() { - let cryptde = CRYPTDE_PAIR.main.as_ref(); - - let subject = Route { hops: vec![] }; - - assert_eq!( - subject.id(cryptde), - Err("Response route did not contain a return route ID".to_string()) - ); - } - - #[test] - #[should_panic(expected = "Could not decrypt with ebe5f9a0e2 data beginning with ebe5f9a0e1")] - fn id_returns_error_when_the_id_fails_to_decrypt() { - let cryptde1 = CryptDENull::from(&PublicKey::new(b"key a"), TEST_DEFAULT_CHAIN); - let cryptde2 = CryptDENull::from(&PublicKey::new(b"key b"), TEST_DEFAULT_CHAIN); - let subject = Route { - hops: vec![Route::encrypt_return_route_id(42, &cryptde1)], - }; - - let _ = subject.id(&cryptde2); - } - - #[test] - fn route_segments_are_represented_in_base64_by_debug() { - let public_key_data_1: Vec = vec![12, 34, 56, 78, 90]; - let public_key_data_2: Vec = vec![34, 56, 78, 90, 12]; - let public_key_data_3: Vec = vec![56, 78, 90, 12, 34]; - let subject = RouteSegment::new( - vec![ - &PublicKey::new(public_key_data_1.as_slice()), - &PublicKey::new(public_key_data_2.as_slice()), - &PublicKey::new(public_key_data_3.as_slice()), - ], - Component::ProxyClient, - ); - - let result = format!("{:?}", subject); - - let base64_1 = base64::encode_config(&public_key_data_1, base64::STANDARD_NO_PAD); - let base64_2 = base64::encode_config(&public_key_data_2, base64::STANDARD_NO_PAD); - let base64_3 = base64::encode_config(&public_key_data_3, base64::STANDARD_NO_PAD); - assert_eq!( - result, - format!("{} -> {} -> {} : ProxyClient", base64_1, base64_2, base64_3) - ); - } - #[test] fn construct_does_not_like_route_segments_with_too_few_keys() { let cryptde = CRYPTDE_PAIR.main.as_ref(); @@ -458,7 +358,6 @@ mod tests { RouteSegment::new(vec![&c_key, &d_key], Component::ProxyServer), cryptde, Some(paying_wallet.clone()), - 0, Some(TEST_DEFAULT_CHAIN.rec().contract), ) .err() @@ -502,7 +401,6 @@ mod tests { let f_key = PublicKey::new(&[70, 70, 70]); let cryptde = CRYPTDE_PAIR.main.as_ref(); let paying_wallet = make_paying_wallet(b"wallet"); - let return_route_id = 4321; let contract_address = TEST_DEFAULT_CHAIN.rec().contract; let subject = Route::round_trip( @@ -510,7 +408,6 @@ mod tests { RouteSegment::new(vec![&d_key, &e_key, &f_key, &a_key], Component::ProxyServer), cryptde, Some(paying_wallet.clone()), - return_route_id, Some(contract_address.clone()), ) .unwrap(); @@ -599,12 +496,6 @@ mod tests { .unwrap(), "seventh hop" ); - - assert_eq!( - subject.hops[7], - Route::encrypt_return_route_id(return_route_id, cryptde), - "eighth hop" - ); } #[test] @@ -783,7 +674,6 @@ mod tests { RouteSegment::new(vec![&key2, &key1], Component::ProxyServer), cryptde, Some(paying_wallet), - 1234, Some(TEST_DEFAULT_CHAIN.rec().contract), ) .unwrap(); @@ -832,13 +722,13 @@ Encrypted with 0x03040506: LiveHop { public_key: 0x, payer: Some(Payer { wallet: let key1 = PublicKey::new(&[1, 2, 3, 4]); let key2 = PublicKey::new(&[2, 3, 4, 5]); let key3 = PublicKey::new(&[3, 4, 5, 6]); + let cryptde = CryptDENull::from(&key1, TEST_DEFAULT_CHAIN); let paying_wallet = make_paying_wallet(b"wallet"); let subject = Route::round_trip( RouteSegment::new(vec![&key1, &key2, &key3], Component::ProxyClient), RouteSegment::new(vec![&key3, &key2, &key1], Component::ProxyServer), - &CryptDENull::from(&key1, TEST_DEFAULT_CHAIN), + &cryptde, Some(paying_wallet), - 1234, Some(TEST_DEFAULT_CHAIN.rec().contract), ) .unwrap(); @@ -861,7 +751,6 @@ Encrypted with 0x02030405: LiveHop { public_key: 0x03040506, payer: Some(Payer { Encrypted with 0x03040506: LiveHop { public_key: 0x02030405, payer: Some(Payer { wallet: Wallet { kind: Address(0x71d0fc7d1c570b1ed786382b551a09391c91e33d) }, proof: Signature { v: 1, r: "8649b8f6db6232cb1e4f1f04786ad4ef33488c968e64bec74ecd893d6d05c1b9", s: "8649b8f6db6232cb1e4f1f04786ad4ef33488c968e64bec74ecd893d6d05c1b9" } }), component: ProxyClient } Encrypted with 0x02030405: LiveHop { public_key: 0x01020304, payer: Some(Payer { wallet: Wallet { kind: Address(0x71d0fc7d1c570b1ed786382b551a09391c91e33d) }, proof: Signature { v: 0, r: "4324a40295bb36ef2b927fb24250fe42397a57b861ea152bbbe4f84150d4ff5a", s: "4324a40295bb36ef2b927fb24250fe42397a57b861ea152bbbe4f84150d4ff5a" } }), component: Hopper } Encrypted with 0x01020304: LiveHop { public_key: 0x, payer: Some(Payer { wallet: Wallet { kind: Address(0x71d0fc7d1c570b1ed786382b551a09391c91e33d) }, proof: Signature { v: 0, r: "3e3a92d7284c2c2ff7119e9f7a7e183b062a335a598e965a47c36a2f288b6f8d", s: "3e3a92d7284c2c2ff7119e9f7a7e183b062a335a598e965a47c36a2f288b6f8d" } }), component: ProxyServer } -Encrypted with 0x01020304: Return Route ID: 1234 "# ) ); diff --git a/node/src/sub_lib/sequence_buffer.rs b/node/src/sub_lib/sequence_buffer.rs index c73dbc021..2cb101c41 100644 --- a/node/src/sub_lib/sequence_buffer.rs +++ b/node/src/sub_lib/sequence_buffer.rs @@ -38,7 +38,7 @@ impl<'a> From<&'a TransmitDataMsg> for SequencedPacket { fn from(tdm: &'a TransmitDataMsg) -> Self { SequencedPacket::new( tdm.data.clone(), - tdm.sequence_number.unwrap_or(0), + tdm.sequence_number_opt.unwrap_or(0), tdm.last_data, ) } @@ -253,7 +253,7 @@ mod tests { endpoint: Endpoint::Socket(SocketAddr::from_str("1.2.3.4:80").unwrap()), last_data: true, data: vec![1, 4, 5, 9], - sequence_number: None, + sequence_number_opt: None, }; let result = SequencedPacket::from(&tdm); @@ -267,7 +267,7 @@ mod tests { endpoint: Endpoint::Socket(SocketAddr::from_str("1.2.3.4:80").unwrap()), last_data: true, data: vec![1, 4, 5, 9], - sequence_number: Some(1), + sequence_number_opt: Some(1), }; let result = SequencedPacket::from(&tdm); @@ -280,7 +280,7 @@ mod tests { endpoint: Endpoint::Socket(SocketAddr::from_str("1.2.3.4:80").unwrap()), last_data: false, data: vec![4, 2, 5, 67], - sequence_number: Some(4), + sequence_number_opt: Some(4), }; let result = SequencedPacket::from(&tdm); diff --git a/node/src/sub_lib/stream_handler_pool.rs b/node/src/sub_lib/stream_handler_pool.rs index fcb0f2e64..92ade9931 100644 --- a/node/src/sub_lib/stream_handler_pool.rs +++ b/node/src/sub_lib/stream_handler_pool.rs @@ -8,7 +8,7 @@ use actix::Message; pub struct TransmitDataMsg { pub endpoint: Endpoint, pub last_data: bool, - pub sequence_number: Option, // Some implies clear data; None implies clandestine. + pub sequence_number_opt: Option, // Some implies clear data; None implies clandestine. pub data: Vec, } diff --git a/node/src/sub_lib/stream_key.rs b/node/src/sub_lib/stream_key.rs index bca70efe4..8e6629ab5 100644 --- a/node/src/sub_lib/stream_key.rs +++ b/node/src/sub_lib/stream_key.rs @@ -93,13 +93,22 @@ impl StreamKey { hash: hash.digest().bytes(), } } + + #[cfg(test)] + pub fn from_bytes(bytes: &[u8]) -> StreamKey { + let mut hash = [0xA; sha1::DIGEST_LENGTH]; + for i in 0..std::cmp::min(sha1::DIGEST_LENGTH, bytes.len()) { + hash[i] = bytes[i]; + } + StreamKey { hash } + } } impl StreamKey { pub fn make_meaningless_stream_key() -> StreamKey { - StreamKey { - hash: [0; sha1::DIGEST_LENGTH], - } + let mut bytes = [0; sha1::DIGEST_LENGTH]; + randombytes_into(&mut bytes); + StreamKey { hash: bytes } } pub fn make_meaningful_stream_key(phrase: &str) -> StreamKey { diff --git a/node/src/sub_lib/ttl_hashmap.rs b/node/src/sub_lib/ttl_hashmap.rs index faa79b68a..4782e78a3 100644 --- a/node/src/sub_lib/ttl_hashmap.rs +++ b/node/src/sub_lib/ttl_hashmap.rs @@ -8,6 +8,7 @@ use std::rc::Rc; use std::time::Duration; use std::time::Instant; +#[allow(clippy::type_complexity)] pub struct TtlHashMap where K: Hash + Clone, @@ -15,6 +16,7 @@ where last_check: RefCell, data: RefCell, Instant)>>, ttl: Duration, + retire_closure: Box bool>, } impl TtlHashMap @@ -27,6 +29,19 @@ where last_check: RefCell::new(Instant::now()), data: RefCell::new(HashMap::new()), ttl, + retire_closure: Box::new(|_, _| true), + } + } + + pub fn new_with_retire(ttl: Duration, retire_closure: F) -> TtlHashMap + where + F: 'static + Fn(&K, &V) -> bool, + { + TtlHashMap { + last_check: RefCell::new(Instant::now()), + data: RefCell::new(HashMap::new()), + ttl, + retire_closure: Box::new(retire_closure), } } @@ -54,6 +69,12 @@ where } } + pub fn remove(&self, key: &K) -> Option> { + self.remove_expired_entries(); + + self.data.borrow_mut().remove(key).map(|(result, _)| result) + } + fn remove_expired_entries(&self) { let now = Instant::now(); @@ -73,8 +94,16 @@ where .collect() }; + let mut data = self.data.borrow_mut(); expired.iter().for_each(|key| { - self.data.borrow_mut().remove(key); + match data.remove(key) { + Some((value, _)) => { + if !(self.retire_closure)(key, value.as_ref()) { + data.insert(key.clone(), (value, now)); + } + } + None => (), // already removed + } }); } } @@ -82,26 +111,70 @@ where #[cfg(test)] mod tests { use super::*; + use std::sync::{Arc, Mutex}; use std::thread; #[test] fn new_sets_ttl() { let subject = TtlHashMap::::new(Duration::from_millis(1000)); - assert_eq!(subject.ttl, Duration::from_millis(1000)); + let result = subject.ttl; + + assert_eq!(result, Duration::from_millis(1000)); } #[test] - fn remove_returns_none_for_entry_that_was_never_inserted() { + fn get_returns_none_for_entry_that_was_never_inserted() { let subject = TtlHashMap::::new(Duration::from_millis(1000)); - assert_eq!(subject.get(&11u32), None); + let result = subject.get(&11u32); + + assert_eq!(result, None); assert_eq!(subject.ttl(), Duration::from_millis(1000)); } #[test] - fn ttl_hashmap_does_not_remove_entry_before_it_is_expired() { + fn remove_returns_none_if_no_such_entry_exists() { + let subject = TtlHashMap::::new(Duration::from_millis(1000)); + + let result = subject.remove(&11u32); + + assert_eq!(result, None); + } + + #[test] + fn remove_returns_existing_entry_and_removes() { + let mut subject = TtlHashMap::::new(Duration::from_millis(1000)); + subject.insert(11u32, 42u32); + + let before_result = subject.remove(&11u32); + let after_result = subject.remove(&11u32); + + assert_eq!(before_result, Some(Rc::new(42u32))); + assert_eq!(after_result, None); + } + + #[test] + fn ttl_hashmap_remove_removes_expired_entry() { let mut subject = TtlHashMap::new(Duration::from_millis(10)); + subject.insert(42u32, "Hello"); + thread::sleep(Duration::from_millis(20)); + + let result = subject.remove(&11u32); // nonexistent key + + assert_eq!(result, None); + // Low-level get, because high-level get would remove it if .remove() didn't + assert_eq!(subject.data.borrow().get(&42u32), None); + } + + #[test] + fn ttl_hashmap_does_not_remove_entry_before_it_is_expired() { + let retire_closure_has_run = Arc::new(Mutex::new(false)); + let retire_closure_has_run_inner = retire_closure_has_run.clone(); + let mut subject = TtlHashMap::new_with_retire(Duration::from_millis(10), move |_, _| { + *(retire_closure_has_run_inner.lock().unwrap()) = true; + true + }); subject.insert(42u32, "Hello"); subject.insert(24u32, "World"); @@ -109,25 +182,52 @@ mod tests { assert_eq!(subject.get(&42u32).unwrap().as_ref(), &"Hello"); assert_eq!(subject.get(&24u32).unwrap().as_ref(), &"World"); assert_eq!(subject.ttl(), Duration::from_millis(10)); + assert_eq!(*retire_closure_has_run.lock().unwrap(), false); } #[test] fn ttl_hashmap_get_removes_expired_entry() { - let mut subject = TtlHashMap::new(Duration::from_millis(10)); - + let retire_closure_has_run = Arc::new(Mutex::new(false)); + let retire_closure_has_run_inner = retire_closure_has_run.clone(); + let mut subject = TtlHashMap::new_with_retire(Duration::from_millis(10), move |_, _| { + *(retire_closure_has_run_inner.lock().unwrap()) = true; + true + }); subject.insert(42u32, "Hello"); + thread::sleep(Duration::from_millis(20)); + + let result = subject.get(&42u32); + + assert_eq!(result, None); + assert_eq!(*retire_closure_has_run.lock().unwrap(), true); + } + #[test] + fn ttl_hashmap_get_does_not_remove_expired_entry_if_closure_returns_false() { + let retire_closure_has_run = Arc::new(Mutex::new(false)); + let retire_closure_has_run_inner = retire_closure_has_run.clone(); + let mut subject = TtlHashMap::new_with_retire(Duration::from_millis(10), move |_, _| { + *(retire_closure_has_run_inner.lock().unwrap()) = true; + false + }); + subject.insert(42u32, "Hello"); thread::sleep(Duration::from_millis(20)); - assert_eq!(subject.get(&42u32), None); + let result = subject.get(&42u32); + + assert_eq!(result, Some(Rc::new("Hello"))); + assert_eq!(*retire_closure_has_run.lock().unwrap(), true); } #[test] fn ttl_hashmap_insert_removes_expired_entry() { - let mut subject = TtlHashMap::new(Duration::from_millis(10)); - + let retire_closure_has_run = Arc::new(Mutex::new(false)); + let retire_closure_has_run_inner = retire_closure_has_run.clone(); + let mut subject = TtlHashMap::new_with_retire(Duration::from_millis(10), move |_, _| { + *(retire_closure_has_run_inner.lock().unwrap()) = true; + true + }); subject.insert(42u32, "Hello"); - thread::sleep(Duration::from_millis(20)); subject.insert(24u32, "World"); @@ -137,6 +237,7 @@ mod tests { subject.data.borrow().get(&24u32).unwrap().0.as_ref(), &"World" ); + assert_eq!(*retire_closure_has_run.lock().unwrap(), true); } #[test] diff --git a/node/src/test_utils/mod.rs b/node/src/test_utils/mod.rs index 351b27711..73c5e5044 100644 --- a/node/src/test_utils/mod.rs +++ b/node/src/test_utils/mod.rs @@ -21,7 +21,6 @@ use crate::blockchain::bip32::Bip32EncryptionKeyProvider; use crate::blockchain::payer::Payer; use crate::bootstrapper::CryptDEPair; use crate::sub_lib::cryptde::CryptDE; -use crate::sub_lib::cryptde::CryptData; use crate::sub_lib::cryptde::PlainData; use crate::sub_lib::cryptde::PublicKey; use crate::sub_lib::dispatcher::Component; @@ -52,8 +51,10 @@ use std::iter::repeat; use std::net::{Shutdown, TcpStream}; use crate::sub_lib::hopper::MessageType; +use crate::sub_lib::host::Host; use crate::sub_lib::proxy_client::DnsResolveFailure_0v1; use crate::sub_lib::stream_key::StreamKey; +use masq_lib::constants::{HTTP_PORT, TLS_PORT}; use std::str::FromStr; use std::sync::{Arc, Mutex}; use std::thread; @@ -195,25 +196,26 @@ pub fn make_meaningless_wallet_private_key() -> PlainData { } // TODO: The three functions below should use only one argument, cryptde -pub fn route_to_proxy_client(main_key: &PublicKey, main_cryptde: &dyn CryptDE) -> Route { +pub fn route_to_proxy_client(main_key: &PublicKey, main_cryptde: &dyn CryptDE, tls: bool) -> Route { shift_one_hop( - zero_hop_route_response(main_key, main_cryptde).route, + zero_hop_route_response(main_key, main_cryptde, tls).route, main_cryptde, ) } -pub fn route_from_proxy_client(key: &PublicKey, cryptde: &dyn CryptDE) -> Route { +pub fn route_from_proxy_client(key: &PublicKey, cryptde: &dyn CryptDE, tls: bool) -> Route { // Happens to be the same - route_to_proxy_client(key, cryptde) + route_to_proxy_client(key, cryptde, tls) } -pub fn route_to_proxy_server(key: &PublicKey, cryptde: &dyn CryptDE) -> Route { - shift_one_hop(route_from_proxy_client(key, cryptde), cryptde) +pub fn route_to_proxy_server(key: &PublicKey, cryptde: &dyn CryptDE, tls: bool) -> Route { + shift_one_hop(route_from_proxy_client(key, cryptde, tls), cryptde) } pub fn zero_hop_route_response( public_key: &PublicKey, cryptde: &dyn CryptDE, + tls: bool, ) -> RouteQueryResponse { RouteQueryResponse { route: Route::round_trip( @@ -221,15 +223,14 @@ pub fn zero_hop_route_response( RouteSegment::new(vec![public_key, public_key], Component::ProxyServer), cryptde, None, - 0, None, ) .unwrap(), expected_services: ExpectedServices::RoundTrip( vec![ExpectedService::Nothing, ExpectedService::Nothing], vec![ExpectedService::Nothing, ExpectedService::Nothing], - 0, ), + host: Host::new("booga.com", if tls { TLS_PORT } else { HTTP_PORT }), } } @@ -238,13 +239,6 @@ fn shift_one_hop(mut route: Route, cryptde: &dyn CryptDE) -> Route { route } -pub fn encrypt_return_route_id(return_route_id: u32, cryptde: &dyn CryptDE) -> CryptData { - let return_route_id_ser = serde_cbor::ser::to_vec(&return_route_id).unwrap(); - cryptde - .encode(cryptde.public_key(), &PlainData::from(return_route_id_ser)) - .unwrap() -} - pub fn make_garbage_data(bytes: usize) -> Vec { let mut data = vec![0; bytes]; rand::thread_rng().fill_bytes(&mut data); @@ -429,8 +423,8 @@ pub fn read_until_timeout(stream: &mut dyn Read) -> Vec { response } -pub fn make_meaningless_message_type() -> MessageType { - DnsResolveFailure_0v1::new(StreamKey::make_meaningless_stream_key()).into() +pub fn make_meaningless_message_type(stream_key: StreamKey) -> MessageType { + DnsResolveFailure_0v1::new(stream_key).into() } pub fn handle_connection_error(stream: TcpStream) { @@ -694,7 +688,7 @@ pub mod unshared_test_utils { ClientRequestPayload_0v1 { stream_key: StreamKey::make_meaningful_stream_key("request"), sequenced_packet: SequencedPacket::new(make_garbage_data(bytes), 0, true), - target_hostname: Some("www.example.com".to_string()), + target_hostname: "www.example.com".to_string(), target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: cryptde.public_key().clone(), @@ -1231,7 +1225,7 @@ mod tests { let cryptde = CRYPTDE_PAIR.main.as_ref(); let key = cryptde.public_key(); - let subject = zero_hop_route_response(&key, cryptde); + let subject = zero_hop_route_response(&key, cryptde, false); assert_eq!( subject.route.hops, @@ -1245,7 +1239,6 @@ mod tests { LiveHop::new(&PublicKey::new(b""), None, Component::ProxyServer) .encode(&key, cryptde) .unwrap(), - encrypt_return_route_id(0, cryptde), ) ); assert_eq!( @@ -1253,7 +1246,6 @@ mod tests { ExpectedServices::RoundTrip( vec![ExpectedService::Nothing, ExpectedService::Nothing,], vec![ExpectedService::Nothing, ExpectedService::Nothing,], - 0 ) ); } @@ -1263,7 +1255,7 @@ mod tests { let cryptde = CRYPTDE_PAIR.main.as_ref(); let key = cryptde.public_key(); - let subject = route_to_proxy_client(&key, cryptde); + let subject = route_to_proxy_client(&key, cryptde, false); let mut garbage_can: Vec = iter::repeat(0u8).take(96).collect(); cryptde.random(&mut garbage_can[..]); @@ -1276,7 +1268,6 @@ mod tests { LiveHop::new(&PublicKey::new(b""), None, Component::ProxyServer) .encode(&key, cryptde) .unwrap(), - encrypt_return_route_id(0, cryptde), CryptData::new(&garbage_can[..]) ) ); @@ -1287,7 +1278,7 @@ mod tests { let cryptde = CRYPTDE_PAIR.main.as_ref(); let key = cryptde.public_key(); - let subject = route_from_proxy_client(&key, cryptde); + let subject = route_from_proxy_client(&key, cryptde, false); let mut garbage_can: Vec = iter::repeat(0u8).take(96).collect(); cryptde.random(&mut garbage_can[..]); @@ -1300,7 +1291,6 @@ mod tests { LiveHop::new(&PublicKey::new(b""), None, Component::ProxyServer) .encode(&key, cryptde) .unwrap(), - encrypt_return_route_id(0, cryptde), CryptData::new(&garbage_can[..]) ) ); @@ -1311,7 +1301,7 @@ mod tests { let cryptde = CRYPTDE_PAIR.main.as_ref(); let key = cryptde.public_key(); - let subject = route_to_proxy_server(&key, cryptde); + let subject = route_to_proxy_server(&key, cryptde, false); let mut first_garbage_can: Vec = iter::repeat(0u8).take(96).collect(); let mut second_garbage_can: Vec = iter::repeat(0u8).take(96).collect(); @@ -1323,7 +1313,6 @@ mod tests { LiveHop::new(&PublicKey::new(b""), None, Component::ProxyServer) .encode(&key, cryptde) .unwrap(), - encrypt_return_route_id(0, cryptde), CryptData::new(&first_garbage_can[..]), CryptData::new(&second_garbage_can[..]), ) diff --git a/node/src/test_utils/recorder.rs b/node/src/test_utils/recorder.rs index f66125182..20daaec86 100644 --- a/node/src/test_utils/recorder.rs +++ b/node/src/test_utils/recorder.rs @@ -39,10 +39,8 @@ use crate::sub_lib::peer_actors::PeerActors; use crate::sub_lib::peer_actors::{BindMessage, NewPublicIp, StartMessage}; use crate::sub_lib::proxy_client::{ClientResponsePayload_0v1, InboundServerData}; use crate::sub_lib::proxy_client::{DnsResolveFailure_0v1, ProxyClientSubs}; -use crate::sub_lib::proxy_server::{ - AddReturnRouteMessage, ClientRequestPayload_0v1, StreamKeyPurge, -}; use crate::sub_lib::proxy_server::{AddRouteResultMessage, ProxyServerSubs}; +use crate::sub_lib::proxy_server::{ClientRequestPayload_0v1, StreamKeyPurge}; use crate::sub_lib::stream_handler_pool::DispatcherNodeQueryResponse; use crate::sub_lib::stream_handler_pool::TransmitDataMsg; use crate::sub_lib::ui_gateway::UiGatewaySubs; @@ -123,7 +121,6 @@ macro_rules! recorder_message_handler_t_p { }; } -recorder_message_handler_t_m_p!(AddReturnRouteMessage); recorder_message_handler_t_m_p!(AddRouteResultMessage); recorder_message_handler_t_p!(AddStreamMsg); recorder_message_handler_t_m_p!(BindMessage); @@ -398,7 +395,6 @@ pub fn make_proxy_server_subs_from_recorder(addr: &Addr) -> ProxyServe from_dispatcher: recipient!(addr, InboundClientData), from_hopper: recipient!(addr, ExpiredCoresPackage), dns_failure_from_hopper: recipient!(addr, ExpiredCoresPackage), - add_return_route: recipient!(addr, AddReturnRouteMessage), stream_shutdown_sub: recipient!(addr, StreamShutdownMsg), node_from_ui: recipient!(addr, NodeFromUiMessage), route_result_sub: recipient!(addr, AddRouteResultMessage), From a71cd1f6e9cd32e223c28253277163a6deb85432 Mon Sep 17 00:00:00 2001 From: Czarte Date: Thu, 6 Nov 2025 18:12:48 +0100 Subject: [PATCH 13/20] Revert "Breaking change to remove RRID from CORES packages (#740)" (#742) Revert "Breaking change to remove RRID from CORES packages (#740)" This reverts commit a151e96a19ec81845f0f7e0415d7b5d7d25c2e99. --- .gitignore | 1 - .../tests/connection_termination_test.rs | 73 +- .../tests/self_test.rs | 11 +- node/src/dispatcher.rs | 28 +- node/src/hopper/consuming_service.rs | 23 +- node/src/hopper/live_cores_package.rs | 22 +- node/src/hopper/mod.rs | 13 +- node/src/hopper/routing_service.rs | 134 +- node/src/neighborhood/mod.rs | 459 +-- node/src/proxy_client/mod.rs | 12 +- node/src/proxy_client/stream_establisher.rs | 8 +- node/src/proxy_client/stream_handler_pool.rs | 155 +- node/src/proxy_client/stream_reader.rs | 26 +- .../client_request_payload_factory.rs | 243 +- node/src/proxy_server/http_protocol_pack.rs | 167 +- node/src/proxy_server/mod.rs | 2979 ++++++++--------- node/src/proxy_server/protocol_pack.rs | 21 +- .../proxy_server/server_impersonator_http.rs | 25 +- .../proxy_server/server_impersonator_tls.rs | 4 +- node/src/proxy_server/tls_protocol_pack.rs | 354 +- node/src/stream_handler_pool.rs | 62 +- node/src/stream_reader.rs | 38 +- node/src/sub_lib/dispatcher.rs | 22 +- node/src/sub_lib/hopper.rs | 11 +- node/src/sub_lib/host.rs | 39 - node/src/sub_lib/http_packet_framer.rs | 2 +- .../migrations/client_request_payload.rs | 14 +- node/src/sub_lib/mod.rs | 1 - node/src/sub_lib/neighborhood.rs | 54 +- node/src/sub_lib/proxy_server.rs | 14 +- node/src/sub_lib/route.rs | 121 +- node/src/sub_lib/sequence_buffer.rs | 8 +- node/src/sub_lib/stream_handler_pool.rs | 2 +- node/src/sub_lib/stream_key.rs | 15 +- node/src/sub_lib/ttl_hashmap.rs | 123 +- node/src/test_utils/mod.rs | 45 +- node/src/test_utils/recorder.rs | 6 +- 37 files changed, 2292 insertions(+), 3043 deletions(-) delete mode 100644 node/src/sub_lib/host.rs diff --git a/.gitignore b/.gitignore index 666a5a098..88a2ab5f9 100644 --- a/.gitignore +++ b/.gitignore @@ -4,7 +4,6 @@ **/*.rs.bk .idea/azure/ .idea/inspectionProfiles/Project_Default.xml -.idea/copilot.data.migration.* ### Node node_modules diff --git a/multinode_integration_tests/tests/connection_termination_test.rs b/multinode_integration_tests/tests/connection_termination_test.rs index 287f2f120..2657661b8 100644 --- a/multinode_integration_tests/tests/connection_termination_test.rs +++ b/multinode_integration_tests/tests/connection_termination_test.rs @@ -1,7 +1,6 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. use masq_lib::blockchains::chains::Chain; -use masq_lib::constants::HTTP_PORT; use masq_lib::test_utils::utils::TEST_DEFAULT_MULTINODE_CHAIN; use masq_lib::utils::find_free_port; use multinode_integration_tests_lib::masq_mock_node::MASQMockNode; @@ -87,12 +86,13 @@ fn reported_server_drop() { let (_, _, lcp) = mock_node .wait_for_package(&masquerader, Duration::from_secs(2)) .unwrap(); - let stream_key = stream_key_from_request_lcp(lcp, &exit_cryptde); + let (stream_key, return_route_id) = + context_from_request_lcp(lcp, real_node.main_cryptde_null().unwrap(), &exit_cryptde); mock_node .transmit_package( mock_node.port_list()[0], - create_server_drop_report(&mock_node, &real_node, stream_key), + create_server_drop_report(&mock_node, &real_node, stream_key, return_route_id), &masquerader, real_node.main_public_key(), real_node.socket_addr(PortSelector::First), @@ -115,7 +115,7 @@ fn actual_server_drop() { let server_port = find_free_port(); let mut server = real_node.make_server(server_port); let masquerader = JsonMasquerader::new(); - let stream_key = arbitrary_stream_key(); + let (stream_key, return_route_id) = arbitrary_context(); let index: u64 = 0; request_server_payload( index, @@ -125,6 +125,7 @@ fn actual_server_drop() { &mut server, &masquerader, stream_key, + return_route_id, ); let index: u64 = 1; request_server_payload( @@ -135,6 +136,7 @@ fn actual_server_drop() { &mut server, &masquerader, stream_key, + return_route_id, ); server.shutdown(); @@ -172,6 +174,7 @@ fn request_server_payload( server: &mut MASQNodeServer, masquerader: &JsonMasquerader, stream_key: StreamKey, + return_route_id: u32, ) { mock_node .transmit_package( @@ -181,6 +184,7 @@ fn request_server_payload( &mock_node, &real_node, stream_key, + return_route_id, &server, cluster.chain, ), @@ -208,7 +212,7 @@ fn reported_client_drop() { let server_port = find_free_port(); let mut server = real_node.make_server(server_port); let masquerader = JsonMasquerader::new(); - let stream_key = arbitrary_stream_key(); + let (stream_key, return_route_id) = arbitrary_context(); let index: u64 = 0; mock_node .transmit_package( @@ -218,6 +222,7 @@ fn reported_client_drop() { &mock_node, &real_node, stream_key, + return_route_id, &server, cluster.chain, ), @@ -235,7 +240,7 @@ fn reported_client_drop() { mock_node .transmit_package( mock_node.port_list()[0], - create_client_drop_report(&mock_node, &real_node, stream_key), + create_client_drop_report(&mock_node, &real_node, stream_key, return_route_id), &masquerader, real_node.main_public_key(), real_node.socket_addr(PortSelector::First), @@ -317,7 +322,11 @@ fn full_neighbor(one: &mut NodeRecord, another: &mut NodeRecord) { .unwrap(); } -fn stream_key_from_request_lcp(lcp: LiveCoresPackage, exit_cryptde: &dyn CryptDE) -> StreamKey { +fn context_from_request_lcp( + lcp: LiveCoresPackage, + originating_cryptde: &dyn CryptDE, + exit_cryptde: &dyn CryptDE, +) -> (StreamKey, u32) { let payload = match decodex::(exit_cryptde, &lcp.payload).unwrap() { MessageType::ClientRequest(vd) => vd .extract(&node_lib::sub_lib::migrations::client_request_payload::MIGRATIONS) @@ -325,11 +334,15 @@ fn stream_key_from_request_lcp(lcp: LiveCoresPackage, exit_cryptde: &dyn CryptDE mt => panic!("Unexpected: {:?}", mt), }; let stream_key = payload.stream_key; - stream_key + let return_route_id = decodex::(originating_cryptde, &lcp.route.hops[6]).unwrap(); + (stream_key, return_route_id) } -fn arbitrary_stream_key() -> StreamKey { - StreamKey::make_meaningful_stream_key("arbitrary_context") +fn arbitrary_context() -> (StreamKey, u32) { + ( + StreamKey::make_meaningful_stream_key("arbitrary_context"), + 12345678, + ) } fn create_request_icp( @@ -337,12 +350,12 @@ fn create_request_icp( originating_node: &MASQMockNode, exit_node: &MASQRealNode, stream_key: StreamKey, + return_route_id: u32, server: &MASQNodeServer, chain: Chain, ) -> IncipientCoresPackage { - let originating_main_cryptde = originating_node.main_cryptde_null().unwrap(); IncipientCoresPackage::new( - originating_main_cryptde, + originating_node.main_cryptde_null().unwrap(), Route::round_trip( RouteSegment::new( vec![ @@ -358,8 +371,9 @@ fn create_request_icp( ], Component::ProxyServer, ), - originating_main_cryptde, + originating_node.main_cryptde_null().unwrap(), originating_node.consuming_wallet(), + return_route_id, Some(chain.rec().contract), ) .unwrap(), @@ -368,7 +382,7 @@ fn create_request_icp( &ClientRequestPayload_0v1 { stream_key, sequenced_packet: SequencedPacket::new(Vec::from(HTTP_REQUEST), index, false), - target_hostname: format!("{}", server.local_addr().ip()), + target_hostname: Some(format!("{}", server.local_addr().ip())), target_port: server.local_addr().port(), protocol: ProxyProtocol::HTTP, originator_public_key: originating_node.main_public_key().clone(), @@ -386,9 +400,8 @@ fn create_meaningless_icp( let socket_addr = SocketAddr::from_str("3.2.1.0:7654").unwrap(); let stream_key = StreamKey::make_meaningful_stream_key("Chancellor on brink of second bailout for banks"); - let main_cryptde = originating_node.main_cryptde_null().unwrap(); IncipientCoresPackage::new( - main_cryptde, + originating_node.main_cryptde_null().unwrap(), Route::round_trip( RouteSegment::new( vec![ @@ -404,8 +417,9 @@ fn create_meaningless_icp( ], Component::ProxyServer, ), - main_cryptde, + originating_node.main_cryptde_null().unwrap(), originating_node.consuming_wallet(), + 1357, Some(TEST_DEFAULT_MULTINODE_CHAIN.rec().contract), ) .unwrap(), @@ -414,7 +428,7 @@ fn create_meaningless_icp( &ClientRequestPayload_0v1 { stream_key, sequenced_packet: SequencedPacket::new(Vec::from(HTTP_REQUEST), 0, false), - target_hostname: "nowhere.com".to_string(), + target_hostname: Some(format!("nowhere.com")), target_port: socket_addr.port(), protocol: ProxyProtocol::HTTP, originator_public_key: originating_node.main_public_key().clone(), @@ -429,9 +443,8 @@ fn create_server_drop_report( exit_node: &MASQMockNode, originating_node: &MASQRealNode, stream_key: StreamKey, + return_route_id: u32, ) -> IncipientCoresPackage { - let exit_main_cryptde = exit_node.main_cryptde_null().unwrap(); - let originating_main_cryptde = originating_node.main_cryptde_null().unwrap(); let mut route = Route::round_trip( RouteSegment::new( vec![ @@ -447,12 +460,15 @@ fn create_server_drop_report( ], Component::ProxyServer, ), - originating_main_cryptde, + originating_node.main_cryptde_null().unwrap(), originating_node.consuming_wallet(), + return_route_id, Some(TEST_DEFAULT_MULTINODE_CHAIN.rec().contract), ) .unwrap(); - route.shift(originating_main_cryptde).unwrap(); + route + .shift(originating_node.main_cryptde_null().unwrap()) + .unwrap(); let payload = MessageType::ClientResponse(VersionedData::new( &node_lib::sub_lib::migrations::client_response_payload::MIGRATIONS, &ClientResponsePayload_0v1 { @@ -462,7 +478,7 @@ fn create_server_drop_report( )); IncipientCoresPackage::new( - exit_main_cryptde, + exit_node.main_cryptde_null().unwrap(), route, payload, originating_node.alias_public_key(), @@ -474,8 +490,8 @@ fn create_client_drop_report( originating_node: &MASQMockNode, exit_node: &MASQRealNode, stream_key: StreamKey, + return_route_id: u32, ) -> IncipientCoresPackage { - let originating_main_cryptde = originating_node.main_cryptde_null().unwrap(); let route = Route::round_trip( RouteSegment::new( vec![ @@ -491,8 +507,9 @@ fn create_client_drop_report( ], Component::ProxyServer, ), - originating_main_cryptde, + originating_node.main_cryptde_null().unwrap(), originating_node.consuming_wallet(), + return_route_id, Some(TEST_DEFAULT_MULTINODE_CHAIN.rec().contract), ) .unwrap(); @@ -501,15 +518,15 @@ fn create_client_drop_report( &ClientRequestPayload_0v1 { stream_key, sequenced_packet: SequencedPacket::new(vec![], 1, true), - target_hostname: String::from("doesnt.matter.com"), - target_port: HTTP_PORT, + target_hostname: Some(String::from("doesnt.matter.com")), + target_port: 80, protocol: ProxyProtocol::HTTP, originator_public_key: originating_node.main_public_key().clone(), }, )); IncipientCoresPackage::new( - originating_main_cryptde, + originating_node.main_cryptde_null().unwrap(), route, payload, exit_node.main_public_key(), diff --git a/multinode_integration_tests/tests/self_test.rs b/multinode_integration_tests/tests/self_test.rs index c19db4c33..973affaf0 100644 --- a/multinode_integration_tests/tests/self_test.rs +++ b/multinode_integration_tests/tests/self_test.rs @@ -16,7 +16,6 @@ use node_lib::sub_lib::dispatcher::Component; use node_lib::sub_lib::hopper::IncipientCoresPackage; use node_lib::sub_lib::route::Route; use node_lib::sub_lib::route::RouteSegment; -use node_lib::sub_lib::stream_key::StreamKey; use node_lib::test_utils::{make_meaningless_message_type, make_paying_wallet}; use std::collections::HashSet; use std::io::ErrorKind; @@ -69,7 +68,6 @@ fn server_relays_cores_package() { let masquerader = JsonMasquerader::new(); let server = MASQCoresServer::new(cluster.chain); let cryptde = server.main_cryptde(); - let stream_key = StreamKey::make_meaningless_stream_key(); let mut client = MASQCoresClient::new(server.local_addr(), cryptde); let mut route = Route::one_way( RouteSegment::new( @@ -84,7 +82,7 @@ fn server_relays_cores_package() { let incipient = IncipientCoresPackage::new( cryptde, route.clone(), - make_meaningless_message_type(stream_key), + make_meaningless_message_type(), &cryptde.public_key(), ) .unwrap(); @@ -101,7 +99,7 @@ fn server_relays_cores_package() { route.shift(cryptde).unwrap(); assert_eq!(expired.remaining_route, route); - assert_eq!(expired.payload, make_meaningless_message_type(stream_key)); + assert_eq!(expired.payload, make_meaningless_message_type()); } #[test] @@ -113,7 +111,6 @@ fn one_mock_node_talks_to_another() { let mock_node_1 = cluster.get_mock_node_by_name("mock_node_1").unwrap(); let mock_node_2 = cluster.get_mock_node_by_name("mock_node_2").unwrap(); let cryptde = CryptDENull::new(TEST_DEFAULT_CHAIN); - let stream_key = StreamKey::make_meaningless_stream_key(); let route = Route::one_way( RouteSegment::new( vec![ @@ -130,7 +127,7 @@ fn one_mock_node_talks_to_another() { let incipient_cores_package = IncipientCoresPackage::new( &cryptde, route, - make_meaningless_message_type(stream_key), + make_meaningless_message_type(), &mock_node_2.main_public_key(), ) .unwrap(); @@ -159,7 +156,7 @@ fn one_mock_node_talks_to_another() { assert_eq!(package_to, mock_node_2.socket_addr(PortSelector::First)); assert_eq!( expired_cores_package.payload, - make_meaningless_message_type(stream_key) + make_meaningless_message_type() ); } diff --git a/node/src/dispatcher.rs b/node/src/dispatcher.rs index 4b01cd593..ac698e043 100644 --- a/node/src/dispatcher.rs +++ b/node/src/dispatcher.rs @@ -271,13 +271,13 @@ mod tests { let recording_arc = proxy_server.get_recording(); let awaiter = proxy_server.get_awaiter(); let client_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); - let reception_port_opt = Some(8080); + let reception_port = Some(8080); let data: Vec = vec![9, 10, 11]; let ibcd_in = InboundClientData { timestamp: SystemTime::now(), client_addr, - reception_port_opt, - sequence_number_opt: Some(0), + reception_port, + sequence_number: Some(0), last_data: false, is_clandestine: false, data: data.clone(), @@ -310,15 +310,15 @@ mod tests { let subject_addr = subject.start(); let (hopper, hopper_awaiter, hopper_recording_arc) = make_recorder(); let client_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); - let reception_port_opt = Some(8080); + let reception_port = Some(8080); let data: Vec = vec![9, 10, 11]; let ibcd_in = InboundClientData { timestamp: SystemTime::now(), client_addr, - reception_port_opt, + reception_port, last_data: false, is_clandestine: true, - sequence_number_opt: None, + sequence_number: None, data: data.clone(), }; let mut peer_actors = peer_actors_builder().hopper(hopper).build(); @@ -350,15 +350,15 @@ mod tests { let subject_addr = subject.start(); let subject_ibcd = subject_addr.recipient::(); let client_addr = SocketAddr::from_str("1.2.3.4:8765").unwrap(); - let reception_port_opt = Some(1234); + let reception_port = Some(1234); let data: Vec = vec![9, 10, 11]; let ibcd_in = InboundClientData { timestamp: SystemTime::now(), client_addr, - reception_port_opt, + reception_port, last_data: false, is_clandestine: false, - sequence_number_opt: Some(0), + sequence_number: Some(0), data: data.clone(), }; @@ -376,15 +376,15 @@ mod tests { let subject_addr = subject.start(); let subject_ibcd = subject_addr.recipient::(); let client_addr = SocketAddr::from_str("1.2.3.4:8765").unwrap(); - let reception_port_opt = Some(1234); + let reception_port = Some(1234); let data: Vec = vec![9, 10, 11]; let ibcd_in = InboundClientData { timestamp: SystemTime::now(), client_addr, - reception_port_opt, + reception_port, last_data: false, is_clandestine: true, - sequence_number_opt: None, + sequence_number: None, data: data.clone(), }; @@ -406,7 +406,7 @@ mod tests { let obcd = TransmitDataMsg { endpoint: Endpoint::Socket(socket_addr), last_data: false, - sequence_number_opt: Some(0), + sequence_number: Some(0), data: data.clone(), }; @@ -430,7 +430,7 @@ mod tests { let obcd = TransmitDataMsg { endpoint: Endpoint::Socket(socket_addr), last_data: false, - sequence_number_opt: None, + sequence_number: None, data: data.clone(), }; let mut peer_actors = peer_actors_builder().build(); diff --git a/node/src/hopper/consuming_service.rs b/node/src/hopper/consuming_service.rs index 0eb34cb52..7d5660a8b 100644 --- a/node/src/hopper/consuming_service.rs +++ b/node/src/hopper/consuming_service.rs @@ -100,10 +100,10 @@ impl ConsumingService { let ibcd = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 0), - reception_port_opt: None, + reception_port: None, last_data: false, is_clandestine: true, - sequence_number_opt: None, + sequence_number: None, data: encrypted_package.into(), }; debug!( @@ -119,7 +119,7 @@ impl ConsumingService { endpoint: next_stop, last_data: false, // Hopper-to-Hopper clandestine streams are never remotely killed data: encrypted_package.into(), - sequence_number_opt: None, + sequence_number: None, }; debug!( @@ -143,7 +143,6 @@ mod tests { use crate::sub_lib::node_addr::NodeAddr; use crate::sub_lib::route::Route; use crate::sub_lib::route::RouteSegment; - use crate::sub_lib::stream_key::StreamKey; use crate::test_utils::recorder::make_recorder; use crate::test_utils::recorder::peer_actors_builder; use crate::test_utils::{make_meaningless_message_type, make_paying_wallet}; @@ -169,7 +168,7 @@ mod tests { CRYPTDE_PAIR.main.as_ref(), &target_key, &target_node_addr, - make_meaningless_message_type(StreamKey::make_meaningless_stream_key()), + make_meaningless_message_type(), ) .unwrap(); let system = System::new(""); @@ -193,7 +192,7 @@ mod tests { &TransmitDataMsg { endpoint: Endpoint::Socket(SocketAddr::from_str("1.2.1.2:1212").unwrap()), last_data: false, - sequence_number_opt: None, + sequence_number: None, data: encodex(CRYPTDE_PAIR.main.as_ref(), &target_key, &lcp) .unwrap() .into(), @@ -243,7 +242,7 @@ mod tests { Some(TEST_DEFAULT_CHAIN.rec().contract), ) .unwrap(); - let payload = make_meaningless_message_type(StreamKey::make_meaningless_stream_key()); + let payload = make_meaningless_message_type(); let incipient_cores_package = IncipientCoresPackage::new(cryptde, route.clone(), payload, &destination_key).unwrap(); let system = System::new("converts_incipient_message_to_live_and_sends_to_dispatcher"); @@ -267,7 +266,7 @@ mod tests { TransmitDataMsg { endpoint: Endpoint::Key(destination_key.clone()), last_data: false, - sequence_number_opt: None, + sequence_number: None, data: expected_lcp_enc.into(), }, *record, @@ -290,7 +289,7 @@ mod tests { Some(TEST_DEFAULT_CHAIN.rec().contract), ) .unwrap(); - let payload = make_meaningless_message_type(StreamKey::make_meaningless_stream_key()); + let payload = make_meaningless_message_type(); let incipient_cores_package = IncipientCoresPackage::new(cryptde, route.clone(), payload, &destination_key).unwrap(); let system = System::new("consume_sends_zero_hop_incipient_directly_to_hopper"); @@ -318,10 +317,10 @@ mod tests { InboundClientData { timestamp: record.timestamp, client_addr: SocketAddr::new(IpAddr::V4(Ipv4Addr::new(127, 0, 0, 1)), 0), - reception_port_opt: None, + reception_port: None, last_data: false, is_clandestine: true, - sequence_number_opt: None, + sequence_number: None, data: expected_lcp_enc.into(), }, ); @@ -341,7 +340,7 @@ mod tests { IncipientCoresPackage::new( CRYPTDE_PAIR.main.as_ref(), Route { hops: vec![] }, - make_meaningless_message_type(StreamKey::make_meaningless_stream_key()), + make_meaningless_message_type(), &PublicKey::new(&[1, 2]), ) .unwrap(), diff --git a/node/src/hopper/live_cores_package.rs b/node/src/hopper/live_cores_package.rs index cac27bebc..2b05b27e8 100644 --- a/node/src/hopper/live_cores_package.rs +++ b/node/src/hopper/live_cores_package.rs @@ -100,7 +100,6 @@ mod tests { use crate::sub_lib::node_addr::NodeAddr; use crate::sub_lib::route::RouteSegment; use crate::sub_lib::route::{Route, RouteError}; - use crate::sub_lib::stream_key::StreamKey; use crate::test_utils::{ make_meaningless_message_type, make_meaningless_route, make_paying_wallet, }; @@ -142,9 +141,7 @@ mod tests { let relay_key = PublicKey::new(&[1, 2]); let relay_cryptde = CryptDENull::from(&relay_key, TEST_DEFAULT_CHAIN); let cryptde = CRYPTDE_PAIR.main.as_ref(); - let stream_key = StreamKey::make_meaningless_stream_key(); - let serialized_payload = - serde_cbor::ser::to_vec(&make_meaningless_message_type(stream_key)).unwrap(); + let serialized_payload = serde_cbor::ser::to_vec(&make_meaningless_message_type()).unwrap(); let encrypted_payload = cryptde .encode(&destination_key, &PlainData::new(&serialized_payload)) .unwrap(); @@ -207,7 +204,7 @@ mod tests { let key34 = PublicKey::new(&[3, 4]); let node_addr34 = NodeAddr::new(&IpAddr::from_str("3.4.3.4").unwrap(), &[1234]); let mut route = Route::single_hop(&key34, cryptde).unwrap(); - let payload = make_meaningless_message_type(StreamKey::make_meaningless_stream_key()); + let payload = make_meaningless_message_type(); let incipient = NoLookupIncipientCoresPackage::new(cryptde, &key34, &node_addr34, payload.clone()) @@ -231,7 +228,7 @@ mod tests { cryptde, &blank_key, &node_addr34, - make_meaningless_message_type(StreamKey::make_meaningless_stream_key()), + make_meaningless_message_type(), ); assert_eq!( @@ -257,7 +254,7 @@ mod tests { Some(contract_address), ) .unwrap(); - let payload = make_meaningless_message_type(StreamKey::make_meaningless_stream_key()); + let payload = make_meaningless_message_type(); let incipient = IncipientCoresPackage::new(cryptde, route.clone(), payload.clone(), &key56).unwrap(); @@ -283,7 +280,7 @@ mod tests { let incipient = IncipientCoresPackage::new( cryptde, Route { hops: vec![] }, - make_meaningless_message_type(StreamKey::make_meaningless_stream_key()), + make_meaningless_message_type(), &PublicKey::new(&[3, 4]), ) .unwrap(); @@ -300,8 +297,7 @@ mod tests { #[test] fn expired_cores_package_can_be_constructed_from_live_cores_package() { let immediate_neighbor_ip = SocketAddr::from_str("1.2.3.4:1234").unwrap(); - let stream_key = StreamKey::make_meaningless_stream_key(); - let payload = make_meaningless_message_type(stream_key); + let payload = make_meaningless_message_type(); let first_stop_key = PublicKey::new(&[3, 4]); let first_stop_cryptde = CryptDENull::from(&first_stop_key, TEST_DEFAULT_CHAIN); let relay_key = PublicKey::new(&[1, 2]); @@ -320,6 +316,7 @@ mod tests { ), cryptde, Some(paying_wallet.clone()), + 1234, Some(contract_address), ) .unwrap(); @@ -378,6 +375,11 @@ mod tests { Component::ProxyServer, ) ); + assert_eq!( + route.hops[0], + crate::test_utils::encrypt_return_route_id(1234, cryptde), + ); + route.hops.remove(0); assert_eq!( &route.hops[0].as_slice()[..8], &[52, 52, 52, 52, 52, 52, 52, 52] diff --git a/node/src/hopper/mod.rs b/node/src/hopper/mod.rs index b2f3d6dc0..588016440 100644 --- a/node/src/hopper/mod.rs +++ b/node/src/hopper/mod.rs @@ -149,7 +149,6 @@ mod tests { use crate::sub_lib::hopper::IncipientCoresPackage; use crate::sub_lib::route::Route; use crate::sub_lib::route::RouteSegment; - use crate::sub_lib::stream_key::StreamKey; use crate::test_utils::unshared_test_utils::prove_that_crash_request_handler_is_hooked_up; use crate::test_utils::{ make_meaningless_message_type, make_paying_wallet, route_to_proxy_client, @@ -176,10 +175,8 @@ mod tests { fn panics_if_routing_service_is_unbound() { let main_cryptde = CRYPTDE_PAIR.main.as_ref(); let client_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); - let route = route_to_proxy_client(&main_cryptde.public_key(), main_cryptde, false); - let stream_key = StreamKey::make_meaningless_stream_key(); - let serialized_payload = - serde_cbor::ser::to_vec(&make_meaningless_message_type(stream_key)).unwrap(); + let route = route_to_proxy_client(&main_cryptde.public_key(), main_cryptde); + let serialized_payload = serde_cbor::ser::to_vec(&make_meaningless_message_type()).unwrap(); let data = main_cryptde .encode( &main_cryptde.public_key(), @@ -196,10 +193,10 @@ mod tests { let inbound_client_data = InboundClientData { timestamp: SystemTime::now(), client_addr, - reception_port_opt: None, + reception_port: None, last_data: false, is_clandestine: false, - sequence_number_opt: None, + sequence_number: None, data: encrypted_package, }; let system = System::new("panics_if_routing_service_is_unbound"); @@ -237,7 +234,7 @@ mod tests { let incipient_package = IncipientCoresPackage::new( main_cryptde, route, - make_meaningless_message_type(StreamKey::make_meaningless_stream_key()), + make_meaningless_message_type(), &main_cryptde.public_key(), ) .unwrap(); diff --git a/node/src/hopper/routing_service.rs b/node/src/hopper/routing_service.rs index 69a88aed4..7f7134d20 100644 --- a/node/src/hopper/routing_service.rs +++ b/node/src/hopper/routing_service.rs @@ -186,10 +186,10 @@ impl RoutingService { let inbound_client_data = InboundClientData { timestamp: ibcd_but_data.timestamp, client_addr: ibcd_but_data.client_addr, - reception_port_opt: ibcd_but_data.reception_port_opt, + reception_port: ibcd_but_data.reception_port, last_data: ibcd_but_data.last_data, is_clandestine: ibcd_but_data.is_clandestine, - sequence_number_opt: ibcd_but_data.sequence_number_opt, + sequence_number: ibcd_but_data.sequence_number, data: payload.into(), }; self.routing_service_subs @@ -495,7 +495,7 @@ impl RoutingService { endpoint: Endpoint::Key(next_hop.public_key), last_data, data: next_live_package_enc.into(), - sequence_number_opt: None, + sequence_number: None, }) } } @@ -542,18 +542,16 @@ mod tests { #[test] fn dns_resolution_failures_are_reported_to_the_proxy_server() { - let route = route_to_proxy_server( - &CRYPTDE_PAIR.main.public_key(), - CRYPTDE_PAIR.main.as_ref(), - false, - ); + let cryptde_pair = CRYPTDE_PAIR.clone(); + let route = + route_to_proxy_server(&cryptde_pair.main.public_key(), cryptde_pair.main.as_ref()); let stream_key = StreamKey::make_meaningless_stream_key(); let dns_resolve_failure = DnsResolveFailure_0v1::new(stream_key); let lcp = LiveCoresPackage::new( route, encodex( - CRYPTDE_PAIR.alias.as_ref(), - &CRYPTDE_PAIR.alias.public_key(), + cryptde_pair.alias.as_ref(), + &cryptde_pair.alias.public_key(), &MessageType::DnsResolveFailed(VersionedData::new( &crate::sub_lib::migrations::dns_resolve_failure::MIGRATIONS, &dns_resolve_failure.clone(), @@ -562,16 +560,16 @@ mod tests { .unwrap(), ); let data_enc = encodex( - CRYPTDE_PAIR.main.as_ref(), - &CRYPTDE_PAIR.main.public_key(), + cryptde_pair.main.as_ref(), + &cryptde_pair.main.public_key(), &lcp, ) .unwrap(); let inbound_client_data = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port_opt: None, - sequence_number_opt: None, + reception_port: None, + sequence_number: None, last_data: false, is_clandestine: false, data: data_enc.into(), @@ -581,7 +579,7 @@ mod tests { let system = System::new("dns_resolution_failures_are_reported_to_the_proxy_server"); let peer_actors = peer_actors_builder().proxy_server(proxy_server).build(); let subject = RoutingService::new( - CRYPTDE_PAIR.clone(), + cryptde_pair, RoutingServiceSubs { proxy_client_subs_opt: peer_actors.proxy_client_opt, proxy_server_subs: peer_actors.proxy_server, @@ -608,38 +606,36 @@ mod tests { #[test] fn logs_and_ignores_message_that_cannot_be_deserialized() { init_test_logging(); - let route = route_from_proxy_client( - &CRYPTDE_PAIR.main.public_key(), - CRYPTDE_PAIR.main.as_ref(), - false, - ); + let cryptde_pair = CRYPTDE_PAIR.clone(); + let route = + route_from_proxy_client(&cryptde_pair.main.public_key(), cryptde_pair.main.as_ref()); let lcp = LiveCoresPackage::new( route, encodex( - CRYPTDE_PAIR.main.as_ref(), - &CRYPTDE_PAIR.main.public_key(), + cryptde_pair.main.as_ref(), + &cryptde_pair.main.public_key(), &[42u8], ) .unwrap(), ); let data_enc = encodex( - CRYPTDE_PAIR.main.as_ref(), - &CRYPTDE_PAIR.main.public_key(), + cryptde_pair.main.as_ref(), + &cryptde_pair.main.public_key(), &lcp, ) .unwrap(); let inbound_client_data = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port_opt: None, - sequence_number_opt: None, + reception_port: None, + sequence_number: None, last_data: false, is_clandestine: false, data: data_enc.into(), }; let peer_actors = peer_actors_builder().build(); let subject = RoutingService::new( - CRYPTDE_PAIR.clone(), + cryptde_pair, RoutingServiceSubs { proxy_client_subs_opt: peer_actors.proxy_client_opt, proxy_server_subs: peer_actors.proxy_server, @@ -665,7 +661,7 @@ mod tests { init_test_logging(); let main_cryptde = CryptDEReal::new(TEST_DEFAULT_CHAIN); let rogue_cryptde = CryptDEReal::new(TEST_DEFAULT_CHAIN); - let route = route_from_proxy_client(main_cryptde.public_key(), &main_cryptde, false); + let route = route_from_proxy_client(main_cryptde.public_key(), &main_cryptde); let lcp = LiveCoresPackage::new( route, encodex(&rogue_cryptde, rogue_cryptde.public_key(), &[42u8]).unwrap(), @@ -674,8 +670,8 @@ mod tests { let inbound_client_data = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port_opt: None, - sequence_number_opt: None, + reception_port: None, + sequence_number: None, last_data: false, is_clandestine: false, data: data_enc.into(), @@ -711,7 +707,7 @@ mod tests { fn logs_and_ignores_message_that_had_invalid_destination() { init_test_logging(); let main_cryptde = CRYPTDE_PAIR.main.as_ref(); - let route = route_from_proxy_client(&main_cryptde.public_key(), main_cryptde, false); + let route = route_from_proxy_client(&main_cryptde.public_key(), main_cryptde); let payload = GossipBuilder::empty(); let lcp = LiveCoresPackage::new( route, @@ -726,8 +722,8 @@ mod tests { let inbound_client_data = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port_opt: None, - sequence_number_opt: None, + reception_port: None, + sequence_number: None, last_data: false, is_clandestine: false, data: data_enc.into(), @@ -757,7 +753,7 @@ mod tests { BAN_CACHE.clear(); let main_cryptde = CRYPTDE_PAIR.main.as_ref(); let (component, _, component_recording_arc) = make_recorder(); - let route = route_to_proxy_client(&main_cryptde.public_key(), main_cryptde, false); + let route = route_to_proxy_client(&main_cryptde.public_key(), main_cryptde); let payload = make_request_payload(0, main_cryptde); let lcp = LiveCoresPackage::new( route, @@ -776,8 +772,8 @@ mod tests { let inbound_client_data = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port_opt: None, - sequence_number_opt: None, + reception_port: None, + sequence_number: None, last_data: true, is_clandestine: false, data: data_enc.into(), @@ -827,7 +823,7 @@ mod tests { init_test_logging(); BAN_CACHE.clear(); let main_cryptde = CRYPTDE_PAIR.main.as_ref(); - let route = route_to_proxy_client(&main_cryptde.public_key(), main_cryptde, false); + let route = route_to_proxy_client(&main_cryptde.public_key(), main_cryptde); let payload = make_request_payload(0, main_cryptde); let lcp = LiveCoresPackage::new( route, @@ -845,8 +841,8 @@ mod tests { let inbound_client_data = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port_opt: None, - sequence_number_opt: None, + reception_port: None, + sequence_number: None, last_data: true, is_clandestine: false, data: data_enc.into(), @@ -885,7 +881,7 @@ mod tests { let main_cryptde = CRYPTDE_PAIR.main.as_ref(); let alias_cryptde = CRYPTDE_PAIR.alias.as_ref(); let (proxy_server, _, proxy_server_recording_arc) = make_recorder(); - let route = route_to_proxy_server(&main_cryptde.public_key(), main_cryptde, false); + let route = route_to_proxy_server(&main_cryptde.public_key(), main_cryptde); let payload = make_response_payload(0); let lcp = LiveCoresPackage::new( route, @@ -901,10 +897,10 @@ mod tests { let inbound_client_data = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.3.2.4:5678").unwrap(), - reception_port_opt: None, + reception_port: None, last_data: false, is_clandestine: true, - sequence_number_opt: None, + sequence_number: None, data: lcp_enc.into(), }; @@ -978,10 +974,10 @@ mod tests { let inbound_client_data = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.3.2.4:5678").unwrap(), - reception_port_opt: None, + reception_port: None, last_data: false, is_clandestine: true, - sequence_number_opt: None, + sequence_number: None, data: data_enc.into(), }; @@ -1051,10 +1047,10 @@ mod tests { let inbound_client_data = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.3.2.4:5678").unwrap(), - reception_port_opt: None, + reception_port: None, last_data: false, is_clandestine: true, - sequence_number_opt: None, + sequence_number: None, data: data_enc.into(), }; @@ -1127,10 +1123,10 @@ mod tests { let inbound_client_data = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port_opt: None, + reception_port: None, last_data: true, is_clandestine: true, - sequence_number_opt: None, + sequence_number: None, data: data_enc.into(), }; @@ -1170,7 +1166,7 @@ mod tests { TransmitDataMsg { endpoint: Endpoint::Key(next_key.clone()), last_data: true, - sequence_number_opt: None, + sequence_number: None, data: expected_lcp_enc.into(), } ); @@ -1219,10 +1215,10 @@ mod tests { let inbound_client_data = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port_opt: None, + reception_port: None, last_data: true, is_clandestine: true, - sequence_number_opt: None, + sequence_number: None, data: data_enc.into(), }; @@ -1262,10 +1258,10 @@ mod tests { InboundClientData { timestamp: record.timestamp, client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port_opt: None, + reception_port: None, last_data: true, is_clandestine: true, - sequence_number_opt: None, + sequence_number: None, data: expected_lcp_enc.into() } ); @@ -1280,7 +1276,7 @@ mod tests { let origin_key = PublicKey::new(&[1, 2]); let origin_cryptde = CryptDENull::from(&origin_key, TEST_DEFAULT_CHAIN); let destination_key = PublicKey::new(&[3, 4]); - let payload = make_meaningless_message_type(StreamKey::make_meaningless_stream_key()); + let payload = make_meaningless_message_type(); let route = Route::one_way( RouteSegment::new( vec![&origin_key, &main_cryptde.public_key(), &destination_key], @@ -1301,10 +1297,10 @@ mod tests { let inbound_client_data = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port_opt: None, + reception_port: None, last_data: true, is_clandestine: true, - sequence_number_opt: None, + sequence_number: None, data: data_enc.into(), }; let system = System::new( @@ -1398,10 +1394,10 @@ mod tests { let inbound_client_data = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port_opt: None, + reception_port: None, last_data: true, is_clandestine: true, - sequence_number_opt: None, + sequence_number: None, data: data_enc.into(), }; let system = System::new( @@ -1574,10 +1570,10 @@ mod tests { let inbound_client_data = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port_opt: None, + reception_port: None, last_data: true, is_clandestine: true, - sequence_number_opt: None, + sequence_number: None, data: data_enc.into(), }; let system = System::new("test"); @@ -1644,10 +1640,10 @@ mod tests { let inbound_client_data = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port_opt: None, + reception_port: None, last_data: true, is_clandestine: true, - sequence_number_opt: None, + sequence_number: None, data: data_enc.into(), }; let system = System::new("test"); @@ -1688,10 +1684,10 @@ mod tests { let inbound_client_data = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port_opt: None, + reception_port: None, last_data: true, is_clandestine: true, - sequence_number_opt: None, + sequence_number: None, data: vec![], }; let system = System::new("consume_logs_error_when_given_bad_input_data"); @@ -1745,10 +1741,10 @@ mod tests { let inbound_client_data = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port_opt: None, + reception_port: None, last_data: true, is_clandestine: true, - sequence_number_opt: None, + sequence_number: None, data: data_enc.into(), }; let system = System::new("consume_logs_error_when_given_bad_input_data"); @@ -1812,10 +1808,10 @@ mod tests { let ibcd = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port_opt: None, + reception_port: None, last_data: true, is_clandestine: true, - sequence_number_opt: None, + sequence_number: None, data: vec![], }; @@ -1877,7 +1873,7 @@ mod tests { &ClientRequestPayload_0v1 { stream_key: StreamKey::make_meaningless_stream_key(), sequenced_packet: SequencedPacket::new(vec![1, 2, 3, 4], 1234, false), - target_hostname: "hostname".to_string(), + target_hostname: Some("hostname".to_string()), target_port: 1234, protocol: ProxyProtocol::TLS, originator_public_key: PublicKey::new(b"1234"), diff --git a/node/src/neighborhood/mod.rs b/node/src/neighborhood/mod.rs index 2345342d8..9c685c0af 100644 --- a/node/src/neighborhood/mod.rs +++ b/node/src/neighborhood/mod.rs @@ -27,15 +27,12 @@ use crate::sub_lib::cryptde::PublicKey; use crate::sub_lib::dispatcher::{Component, StreamShutdownMsg}; use crate::sub_lib::hopper::{ExpiredCoresPackage, NoLookupIncipientCoresPackage}; use crate::sub_lib::hopper::{IncipientCoresPackage, MessageType}; -use crate::sub_lib::host::Host; -use crate::sub_lib::neighborhood::ConnectionProgressEvent; -use crate::sub_lib::neighborhood::ExpectedService::{Exit, Nothing, Routing}; -use crate::sub_lib::neighborhood::ExpectedServices::RoundTrip; use crate::sub_lib::neighborhood::RouteQueryResponse; use crate::sub_lib::neighborhood::UpdateNodeRecordMetadataMessage; use crate::sub_lib::neighborhood::{AskAboutDebutGossipMessage, NodeDescriptor}; use crate::sub_lib::neighborhood::{ConfigChange, RemoveNeighborMessage}; use crate::sub_lib::neighborhood::{ConfigChangeMsg, RouteQueryMessage}; +use crate::sub_lib::neighborhood::{ConnectionProgressEvent, ExpectedServices}; use crate::sub_lib::neighborhood::{ConnectionProgressMessage, ExpectedService}; use crate::sub_lib::neighborhood::{DispatcherNodeQueryMessage, GossipFailure_0v1}; use crate::sub_lib::neighborhood::{Hops, NeighborhoodMetadata, NodeQueryResponseMetadata}; @@ -108,6 +105,7 @@ pub struct Neighborhood { mode: NeighborhoodModeLight, min_hops: Hops, db_patch_size: u8, + next_return_route_id: u32, overall_connection_status: OverallConnectionStatus, chain: Chain, crashable: bool, @@ -432,6 +430,7 @@ impl Neighborhood { mode, min_hops, db_patch_size, + next_return_route_id: 0, overall_connection_status, chain: config.blockchain_bridge_config.chain, crashable: config.crash_point == CrashPoint::Message, @@ -497,9 +496,9 @@ impl Neighborhood { } fn handle_route_query_message(&mut self, msg: RouteQueryMessage) -> Option { - let debug_msg_opt = self.logger.debug_enabled().then(|| format!("{}", msg.host)); + let debug_msg_opt = self.logger.debug_enabled().then(|| format!("{:?}", msg)); let route_result = if self.mode == NeighborhoodModeLight::ZeroHop { - Ok(self.zero_hop_route_response(msg.host)) + Ok(self.zero_hop_route_response()) } else { self.make_round_trip_route(msg) }; @@ -883,7 +882,7 @@ impl Neighborhood { target_component: Component::ProxyClient, return_component_opt: Some(Component::ProxyServer), payload_size: 10000, - host: Host::new("booga.com", 1234), + hostname_opt: None, }; if self.handle_route_query_message(msg).is_some() { debug!( @@ -983,7 +982,8 @@ impl Neighborhood { .expect("route creation error") } - fn zero_hop_route_response(&mut self, host: Host) -> RouteQueryResponse { + fn zero_hop_route_response(&mut self) -> RouteQueryResponse { + let return_route_id = self.advance_return_route_id(); let route = Route::round_trip( RouteSegment::new( vec![self.cryptde.public_key(), self.cryptde.public_key()], @@ -995,13 +995,17 @@ impl Neighborhood { ), self.cryptde.as_ref(), None, + return_route_id, None, ) .expect("Couldn't create route"); RouteQueryResponse { route, - expected_services: RoundTrip(vec![Nothing, Nothing], vec![Nothing, Nothing]), - host, + expected_services: ExpectedServices::RoundTrip( + vec![ExpectedService::Nothing, ExpectedService::Nothing], + vec![ExpectedService::Nothing, ExpectedService::Nothing], + return_route_id, + ), } } @@ -1009,7 +1013,7 @@ impl Neighborhood { &mut self, request_msg: RouteQueryMessage, ) -> Result { - let host = request_msg.host; + let hostname_opt = request_msg.hostname_opt.as_deref(); let over = self.make_route_segment( self.cryptde.public_key(), request_msg.target_key_opt.as_ref(), @@ -1017,7 +1021,7 @@ impl Neighborhood { request_msg.target_component, request_msg.payload_size, RouteDirection::Over, - &host.name, + hostname_opt, )?; debug!(self.logger, "Route over: {:?}", over); // Estimate for routing-undesirability calculations. @@ -1034,17 +1038,16 @@ impl Neighborhood { .expect("No return component"), anticipated_response_payload_len, RouteDirection::Back, - &host.name, + hostname_opt, )?; debug!(self.logger, "Route back: {:?}", back); - self.compose_route_query_response(over, back, host) + self.compose_route_query_response(over, back) } fn compose_route_query_response( &mut self, over: RouteSegment, back: RouteSegment, - host: Host, ) -> Result { let segments = vec![&over, &back]; @@ -1067,17 +1070,22 @@ impl Neighborhood { Err(e) => return Err(e), }; + let return_route_id = self.advance_return_route_id(); Ok(RouteQueryResponse { route: Route::round_trip( over, back, self.cryptde.as_ref(), self.consuming_wallet_opt.clone(), + return_route_id, Some(self.chain.rec().contract), ) .expect("Internal error: bad route"), - expected_services: RoundTrip(expected_request_services, expected_response_services), - host, + expected_services: ExpectedServices::RoundTrip( + expected_request_services, + expected_response_services, + return_route_id, + ), }) } @@ -1090,7 +1098,7 @@ impl Neighborhood { target_component: Component, payload_size: usize, direction: RouteDirection, - hostname: &str, + hostname_opt: Option<&str>, ) -> Result { let route_opt = self.find_best_route_segment( origin, @@ -1098,7 +1106,7 @@ impl Neighborhood { minimum_hop_count, payload_size, direction, - hostname, + hostname_opt, ); match route_opt { None => { @@ -1137,20 +1145,20 @@ impl Neighborhood { match self.neighborhood_database.node_by_key(route_segment_key) { Some(node) => { if route_segment_key == self.neighborhood_database.root().public_key() { - Ok(Nothing) + Ok(ExpectedService::Nothing) } else { match (originator_key, exit_key) { (Some(originator_key), Some(exit_key)) if route_segment_key == originator_key || route_segment_key == exit_key => { - Ok(Exit( + Ok(ExpectedService::Exit( route_segment_key.clone(), node.earning_wallet(), *node.rate_pack(), )) } - (Some(_), Some(_)) => Ok(Routing( + (Some(_), Some(_)) => Ok(ExpectedService::Routing( route_segment_key.clone(), node.earning_wallet(), *node.rate_pack(), @@ -1268,7 +1276,11 @@ impl Neighborhood { UndesirabilityType::Relay => { node_record.inner.rate_pack.routing_charge(payload_size) as i64 } - UndesirabilityType::ExitRequest(hostname) => { + UndesirabilityType::ExitRequest(None) => { + node_record.inner.rate_pack.exit_charge(payload_size) as i64 + + node_record.metadata.country_undesirability as i64 + } + UndesirabilityType::ExitRequest(Some(hostname)) => { let exit_undesirability = node_record.inner.rate_pack.exit_charge(payload_size) as i64; let country_undesirability = node_record.metadata.country_undesirability as i64; @@ -1314,6 +1326,12 @@ impl Neighborhood { } } + fn advance_return_route_id(&mut self) -> u32 { + let return_route_id = self.next_return_route_id; + self.next_return_route_id = return_route_id.wrapping_add(1); + return_route_id + } + pub fn find_exit_locations<'a>( &'a self, source: &'a PublicKey, @@ -1332,7 +1350,7 @@ impl Neighborhood { PAYLOAD_ZERO_SIZE, RouteDirection::Over, &mut minimum_undesirability, - "booga.com", + None, true, research_exits, ); @@ -1354,7 +1372,7 @@ impl Neighborhood { minimum_hops: usize, payload_size: usize, direction: RouteDirection, - hostname: &str, + hostname_opt: Option<&str>, ) -> Option> { let mut minimum_undesirability = i64::MAX; let initial_undesirability = @@ -1371,7 +1389,7 @@ impl Neighborhood { payload_size, direction, &mut minimum_undesirability, - hostname, + hostname_opt, false, &mut vec![], ) @@ -1395,7 +1413,7 @@ impl Neighborhood { payload_size: usize, direction: RouteDirection, minimum_undesirability: &mut i64, - hostname: &str, + hostname_opt: Option<&str>, research_neighborhood: bool, research_exits: &mut Vec<&'a PublicKey>, ) -> Vec> { @@ -1441,7 +1459,7 @@ impl Neighborhood { payload_size, direction, minimum_undesirability, - hostname, + hostname_opt, research_neighborhood, research_exits, previous_node, @@ -1463,7 +1481,7 @@ impl Neighborhood { payload_size, direction, minimum_undesirability, - hostname, + hostname_opt, research_neighborhood, research_exits, previous_node, @@ -1481,7 +1499,7 @@ impl Neighborhood { payload_size: usize, direction: RouteDirection, minimum_undesirability: &mut i64, - hostname: &str, + hostname_opt: Option<&str>, research_neighborhood: bool, exits_research: &mut Vec<&'a PublicKey>, previous_node: &NodeRecord, @@ -1512,7 +1530,7 @@ impl Neighborhood { new_hops_remaining, payload_size as u64, direction, - hostname, + hostname_opt, ); self.routing_engine( @@ -1523,7 +1541,7 @@ impl Neighborhood { payload_size, direction, minimum_undesirability, - hostname, + hostname_opt, research_neighborhood, exits_research, ) @@ -1580,11 +1598,11 @@ impl Neighborhood { hops_remaining: usize, payload_size: u64, direction: RouteDirection, - hostname: &str, + hostname_opt: Option<&str>, ) -> i64 { let undesirability_type = match (direction, target_opt) { (RouteDirection::Over, None) if hops_remaining == 0 => { - UndesirabilityType::ExitRequest(hostname) + UndesirabilityType::ExitRequest(hostname_opt) } (RouteDirection::Over, _) => UndesirabilityType::Relay, // The exit-and-relay undesirability is initial_undesirability @@ -2146,7 +2164,7 @@ impl UserExitPreferences { #[derive(PartialEq, Eq, Debug)] enum UndesirabilityType<'hostname> { Relay, - ExitRequest(&'hostname str), + ExitRequest(Option<&'hostname str>), ExitAndRouteResponse, } @@ -2167,20 +2185,10 @@ impl<'a> ComputedRouteSegment<'a> { #[cfg(test)] mod tests { - use super::*; use actix::Recipient; use actix::System; use itertools::Itertools; use lazy_static::lazy_static; - use masq_lib::constants::{DEFAULT_CHAIN, TLS_PORT}; - use masq_lib::messages::{ - CountryGroups, ToMessageBody, UiConnectionChangeBroadcast, UiConnectionStage, - }; - use masq_lib::test_utils::utils::{ensure_node_home_directory_exists, TEST_DEFAULT_CHAIN}; - use masq_lib::ui_gateway::MessageBody; - use masq_lib::ui_gateway::MessagePath::Conversation; - use masq_lib::ui_gateway::MessageTarget; - use masq_lib::utils::running_test; use serde_cbor; use std::any::TypeId; use std::cell::RefCell; @@ -2195,28 +2203,29 @@ mod tests { use std::time::Instant; use tokio::prelude::Future; - use crate::accountant::test_utils::bc_from_earning_wallet; - use crate::bootstrapper::CryptDEPair; + use masq_lib::constants::{DEFAULT_CHAIN, TLS_PORT}; + use masq_lib::messages::{ + CountryGroups, ToMessageBody, UiConnectionChangeBroadcast, UiConnectionStage, + }; + use masq_lib::test_utils::utils::{ensure_node_home_directory_exists, TEST_DEFAULT_CHAIN}; + use masq_lib::ui_gateway::MessageBody; + use masq_lib::ui_gateway::MessagePath::Conversation; + use masq_lib::ui_gateway::MessageTarget; + use masq_lib::utils::running_test; + use crate::db_config::persistent_configuration::PersistentConfigError; use crate::neighborhood::gossip::Gossip_0v1; use crate::neighborhood::gossip::{GossipBuilder, GossipNodeRecord}; use crate::neighborhood::node_record::{NodeRecordInner_0v1, NodeRecordInputs}; - use crate::neighborhood::overall_connection_status::ConnectionStageErrors::{ - NoGossipResponseReceived, PassLoopFound, TcpConnectionFailed, - }; - use crate::neighborhood::overall_connection_status::{ - ConnectionProgress, ConnectionStage, OverallConnectionStage, - }; use crate::stream_messages::{NonClandestineAttributes, RemovedStreamType}; use crate::sub_lib::cryptde::{decodex, encodex, CryptData, PlainData}; use crate::sub_lib::cryptde_null::CryptDENull; use crate::sub_lib::dispatcher::Endpoint; use crate::sub_lib::hop::LiveHop; use crate::sub_lib::hopper::MessageType; - use crate::sub_lib::host::Host; - use crate::sub_lib::neighborhood::ExpectedServices::OneWay; use crate::sub_lib::neighborhood::{ - AskAboutDebutGossipMessage, ConfigChange, ConfigChangeMsg, NeighborhoodMode, WalletPair, + AskAboutDebutGossipMessage, ConfigChange, ConfigChangeMsg, ExpectedServices, + NeighborhoodMode, WalletPair, }; use crate::sub_lib::neighborhood::{NeighborhoodConfig, DEFAULT_RATE_PACK}; use crate::sub_lib::neighborhood::{NeighborhoodMetadata, RatePack}; @@ -2239,13 +2248,23 @@ mod tests { 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::notify_handlers::NotifyLaterHandleMock; use crate::test_utils::unshared_test_utils::{ assert_on_initialization_with_panic_on_migration, make_cpm_recipient, make_node_to_ui_recipient, make_recipient_and_recording_arc, prove_that_crash_request_handler_is_hooked_up, AssertionsMessage, }; use crate::test_utils::vec_to_set; + + use super::*; + use crate::accountant::test_utils::bc_from_earning_wallet; + use crate::bootstrapper::CryptDEPair; + use crate::neighborhood::overall_connection_status::ConnectionStageErrors::{ + NoGossipResponseReceived, PassLoopFound, TcpConnectionFailed, + }; + use crate::neighborhood::overall_connection_status::{ + ConnectionProgress, ConnectionStage, OverallConnectionStage, + }; + use crate::test_utils::unshared_test_utils::notify_handlers::NotifyLaterHandleMock; use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; lazy_static! { @@ -3157,7 +3176,7 @@ mod tests { } #[test] - pub fn progress_in_the_stage_of_overall_connection_status_made_by_one_cpm_is_not_overridden_by_the_other( + pub fn progress_in_the_stage_of_overall_connection_status_made_by_one_cpm_is_not_overriden_by_the_other( ) { let peer_1 = make_ip(1); let peer_2 = make_ip(2); @@ -3177,7 +3196,7 @@ mod tests { neighborhood_config, make_wallet("earning"), None, - "progress_in_the_stage_of_overall_connection_status_made_by_one_cpm_is_not_overridden_by_the_other"), + "progress_in_the_stage_of_overall_connection_status_made_by_one_cpm_is_not_overriden_by_the_other"), ); let (node_to_ui_recipient, _) = make_node_to_ui_recipient(); subject.node_to_ui_recipient_opt = Some(node_to_ui_recipient); @@ -3290,10 +3309,7 @@ mod tests { let addr: Addr = subject.start(); let sub: Recipient = addr.recipient::(); - let future = sub.send(RouteQueryMessage::data_indefinite_route_request( - Host::new("booga.com", 1234), - 400, - )); + let future = sub.send(RouteQueryMessage::data_indefinite_route_request(None, 400)); System::current().stop_with_code(0); system.run(); @@ -3309,10 +3325,7 @@ mod tests { let addr: Addr = subject.start(); let sub: Recipient = addr.recipient::(); - let future = sub.send(RouteQueryMessage::data_indefinite_route_request( - Host::new("booga.com", 1234), - 430, - )); + let future = sub.send(RouteQueryMessage::data_indefinite_route_request(None, 430)); System::current().stop_with_code(0); system.run(); @@ -3352,8 +3365,7 @@ mod tests { } let addr: Addr = subject.start(); let sub: Recipient = addr.recipient::(); - let msg = - RouteQueryMessage::data_indefinite_route_request(Host::new("booga.com", 1234), 54000); + let msg = RouteQueryMessage::data_indefinite_route_request(None, 54000); let future = sub.send(msg); @@ -3378,28 +3390,29 @@ mod tests { ), cryptde, None, + 0, None, ) .unwrap(), - expected_services: RoundTrip( + expected_services: ExpectedServices::RoundTrip( vec![ - Nothing, - Exit( + ExpectedService::Nothing, + ExpectedService::Exit( desirable_exit_node.public_key().clone(), desirable_exit_node.earning_wallet(), rate_pack(2345), ), ], vec![ - Exit( + ExpectedService::Exit( desirable_exit_node.public_key().clone(), desirable_exit_node.earning_wallet(), rate_pack(2345), ), - Nothing, + ExpectedService::Nothing, ], + 0, ), - host: Host::new("booga.com", 1234), }; assert_eq!(expected_response, result); } @@ -3412,8 +3425,7 @@ mod tests { subject.min_hops = Hops::TwoHops; let addr: Addr = subject.start(); let sub: Recipient = addr.recipient::(); - let msg = - RouteQueryMessage::data_indefinite_route_request(Host::new("booga.com", 1234), 20000); + let msg = RouteQueryMessage::data_indefinite_route_request(None, 20000); let future = sub.send(msg); @@ -3433,8 +3445,7 @@ mod tests { let sub: Recipient = addr.recipient::(); let future = sub.send(RouteQueryMessage::data_indefinite_route_request( - Host::new("google.com", 1234), - 12345, + None, 12345, )); System::current().stop_with_code(0); @@ -3452,15 +3463,39 @@ mod tests { ), cryptde, None, + 0, None, ) .unwrap(), - expected_services: RoundTrip(vec![Nothing, Nothing], vec![Nothing, Nothing]), - host: Host::new("google.com", 1234), + expected_services: ExpectedServices::RoundTrip( + vec![ExpectedService::Nothing, ExpectedService::Nothing], + vec![ExpectedService::Nothing, ExpectedService::Nothing], + 0, + ), }; assert_eq!(result, expected_response); } + #[test] + fn zero_hop_routing_handles_return_route_id_properly() { + let mut subject = make_standard_subject(); + let result0 = subject.zero_hop_route_response(); + let result1 = subject.zero_hop_route_response(); + + let return_route_id_0 = match result0.expected_services { + ExpectedServices::RoundTrip(_, _, id) => id, + _ => panic!("expected RoundTrip got OneWay"), + }; + + let return_route_id_1 = match result1.expected_services { + ExpectedServices::RoundTrip(_, _, id) => id, + _ => panic!("expected RoundTrip got OneWay"), + }; + + assert_eq!(return_route_id_0, 0); + assert_eq!(return_route_id_1, 1); + } + /* Database: @@ -3506,10 +3541,7 @@ mod tests { let addr: Addr = subject.start(); let sub: Recipient = addr.recipient::(); - let data_route = sub.send(RouteQueryMessage::data_indefinite_route_request( - Host::new("booga.com", 1234), - 5000, - )); + let data_route = sub.send(RouteQueryMessage::data_indefinite_route_request(None, 5000)); System::current().stop_with_code(0); system.run(); @@ -3522,24 +3554,41 @@ mod tests { segment(&[r, q, p], &Component::ProxyServer), cryptde, consuming_wallet_opt, + 0, Some(contract_address), ) .unwrap(), - expected_services: RoundTrip( + expected_services: ExpectedServices::RoundTrip( vec![ - Nothing, - Routing(q.public_key().clone(), q.earning_wallet(), rate_pack(3456)), - Exit(r.public_key().clone(), r.earning_wallet(), rate_pack(4567)), + ExpectedService::Nothing, + ExpectedService::Routing( + q.public_key().clone(), + q.earning_wallet(), + rate_pack(3456), + ), + ExpectedService::Exit( + r.public_key().clone(), + r.earning_wallet(), + rate_pack(4567), + ), ], vec![ - Exit(r.public_key().clone(), r.earning_wallet(), rate_pack(4567)), - Routing(q.public_key().clone(), q.earning_wallet(), rate_pack(3456)), - Nothing, + ExpectedService::Exit( + r.public_key().clone(), + r.earning_wallet(), + rate_pack(4567), + ), + ExpectedService::Routing( + q.public_key().clone(), + q.earning_wallet(), + rate_pack(3456), + ), + ExpectedService::Nothing, ], + 0, ), - host: Host::new("booga.com", 1234), }; - assert_eq!(result, expected_response); + assert_eq!(expected_response, result); } #[test] @@ -3549,7 +3598,6 @@ mod tests { let result: Result = subject.compose_route_query_response( RouteSegment::new(vec![], Component::Neighborhood), RouteSegment::new(vec![], Component::Neighborhood), - Host::new("booga.com", 1234), ); assert!(result.is_err()); let error_expectation: String = result.expect_err("Expected an Err but got:"); @@ -3559,6 +3607,18 @@ mod tests { ); } + #[test] + fn next_return_route_id_wraps_around() { + let mut subject = make_standard_subject(); + subject.next_return_route_id = 0xFFFFFFFF; + + let end = subject.advance_return_route_id(); + let beginning = subject.advance_return_route_id(); + + assert_eq!(end, 0xFFFFFFFF); + assert_eq!(beginning, 0x00000000); + } + /* Database: @@ -3567,6 +3627,37 @@ mod tests { Tests will be written from the viewpoint of O. */ + #[test] + fn return_route_ids_increase() { + let cryptde = CRYPTDE_PAIR.main.as_ref(); + let system = System::new("return_route_ids_increase"); + let (_, _, _, mut subject) = make_o_r_e_subject(); + subject.min_hops = Hops::TwoHops; + let addr: Addr = subject.start(); + let sub: Recipient = addr.recipient::(); + + let data_route_0 = sub.send(RouteQueryMessage::data_indefinite_route_request(None, 2000)); + let data_route_1 = sub.send(RouteQueryMessage::data_indefinite_route_request(None, 3000)); + + System::current().stop_with_code(0); + system.run(); + let result_0 = data_route_0.wait().unwrap().unwrap(); + let result_1 = data_route_1.wait().unwrap().unwrap(); + let juicy_parts = |result: RouteQueryResponse| { + let last_element = result.route.hops.last().unwrap(); + let last_element_dec = cryptde.decode(last_element).unwrap(); + let network_return_route_id: u32 = + serde_cbor::de::from_slice(last_element_dec.as_slice()).unwrap(); + let metadata_return_route_id = match result.expected_services { + ExpectedServices::RoundTrip(_, _, id) => id, + _ => panic!("expected RoundTrip got OneWay"), + }; + (network_return_route_id, metadata_return_route_id) + }; + assert_eq!(juicy_parts(result_0), (0, 0)); + assert_eq!(juicy_parts(result_1), (1, 1)); + } + #[test] fn handle_neighborhood_graph_message_works() { let test_name = "handle_neighborhood_graph_message_works"; @@ -4427,7 +4518,6 @@ mod tests { let result: Result = subject.compose_route_query_response( RouteSegment::new(vec![], Component::ProxyClient), RouteSegment::new(vec![], Component::ProxyServer), - Host::new("booga.com", 1234), ); assert!(result.is_err()); let error_expectation: String = result.expect_err("Expected an Err but got:"); @@ -4444,7 +4534,6 @@ mod tests { let result: Result = subject.compose_route_query_response( RouteSegment::new(vec![&PublicKey::new(&[3, 3, 8])], Component::ProxyClient), RouteSegment::new(vec![&PublicKey::new(&[8, 3, 3])], Component::ProxyServer), - Host::new("booga.com", 1234), ); assert!(result.is_err()); let error_expectation: String = result.expect_err("Expected an Err but got:"); @@ -4452,6 +4541,7 @@ mod tests { error_expectation, "Cannot make multi_hop with unknown neighbor" ); + assert_eq!(subject.next_return_route_id, 0); } #[test] @@ -4573,58 +4663,34 @@ mod tests { // At least two hops from p to anywhere standard let route_opt = - subject.find_best_route_segment(p, None, 2, 10000, RouteDirection::Over, "booga.com"); + subject.find_best_route_segment(p, None, 2, 10000, RouteDirection::Over, None); assert_eq!(route_opt.unwrap(), vec![p, s, t]); // no [p, r, s] or [p, s, r] because s and r are both neighbors of p and can't exit for it // At least two hops over from p to t - let route_opt = subject.find_best_route_segment( - p, - Some(t), - 2, - 10000, - RouteDirection::Over, - "booga.com", - ); + let route_opt = + subject.find_best_route_segment(p, Some(t), 2, 10000, RouteDirection::Over, None); assert_eq!(route_opt.unwrap(), vec![p, s, t]); // At least two hops over from t to p - let route_opt = subject.find_best_route_segment( - t, - Some(p), - 2, - 10000, - RouteDirection::Over, - "booga.com", - ); + let route_opt = + subject.find_best_route_segment(t, Some(p), 2, 10000, RouteDirection::Over, None); assert_eq!(route_opt, None); // p is consume-only; can't be an exit Node. // At least two hops back from t to p - let route_opt = subject.find_best_route_segment( - t, - Some(p), - 2, - 10000, - RouteDirection::Back, - "booga.com", - ); + let route_opt = + subject.find_best_route_segment(t, Some(p), 2, 10000, RouteDirection::Back, None); assert_eq!(route_opt.unwrap(), vec![t, s, p]); // p is consume-only, but it's the originating Node, so including it is okay // At least two hops from p to Q - impossible - let route_opt = subject.find_best_route_segment( - p, - Some(q), - 2, - 10000, - RouteDirection::Over, - "booga.com", - ); + let route_opt = + subject.find_best_route_segment(p, Some(q), 2, 10000, RouteDirection::Over, None); assert_eq!(route_opt, None); } @@ -4669,7 +4735,7 @@ mod tests { 3, 10000, RouteDirection::Back, - "booga.com", + None, ) .unwrap(); @@ -4744,7 +4810,7 @@ mod tests { 3, 10000, RouteDirection::Over, - "booga.com", + None, ); let after = Instant::now(); @@ -4795,14 +4861,8 @@ mod tests { db.add_arbitrary_full_neighbor(c_au_key, a_fr_key); subject.handle_exit_location_message(message, 0, 0); - let route_cz = subject.find_best_route_segment( - root_key, - None, - 2, - 10000, - RouteDirection::Over, - "booga.com", - ); + let route_cz = + subject.find_best_route_segment(root_key, None, 2, 10000, RouteDirection::Over, None); assert_eq!(route_cz, None); } @@ -4867,7 +4927,7 @@ mod tests { subject_min_hops, 10000, RouteDirection::Over, - "booga.com", + None, ); let exit_node = cdb.node_by_key(&route_au.as_ref().unwrap().last().unwrap()); @@ -4922,14 +4982,8 @@ mod tests { }; subject.handle_exit_location_message(message, 0, 0); - let route_fr = subject.find_best_route_segment( - root_key, - None, - 2, - 10000, - RouteDirection::Over, - "booga.com", - ); + let route_fr = + subject.find_best_route_segment(root_key, None, 2, 10000, RouteDirection::Over, None); let exit_node = cdb.node_by_key(&route_fr.as_ref().unwrap().last().unwrap()); assert_eq!( @@ -4952,7 +5006,7 @@ mod tests { // At least two hops from P to anywhere standard let route_opt = - subject.find_best_route_segment(p, None, 2, 10000, RouteDirection::Over, "booga.com"); + subject.find_best_route_segment(p, None, 2, 10000, RouteDirection::Over, None); assert_eq!(route_opt, None); } @@ -4969,7 +5023,7 @@ mod tests { 5, // Lots of hops to go yet 1_000, RouteDirection::Over, - "hostname.com", + Some("hostname.com"), ); let rate_pack = node_record.rate_pack(); @@ -4993,7 +5047,7 @@ mod tests { 0, // Last hop 1_000, RouteDirection::Over, - "hostname.com", + Some("hostname.com"), ); let rate_pack = node_record.rate_pack(); @@ -5021,7 +5075,7 @@ mod tests { 0, // Last hop 1_000, RouteDirection::Over, - "hostname.com", + Some("hostname.com"), ); let rate_pack = node_record.rate_pack(); @@ -5094,7 +5148,7 @@ mod tests { 5, // Plenty of hops remaining: not there yet 1_000, RouteDirection::Back, - "booga.com", + None, ); let rate_pack = node_record.rate_pack(); @@ -6568,7 +6622,7 @@ mod tests { target_component: Component::ProxyClient, return_component_opt: None, payload_size: 10000, - host: Host::new("booga.com", 1234), + hostname_opt: None, }; let unsuccessful_three_hop_route = addr.send(three_hop_route_request); let asserted_node_record = a.clone(); @@ -6618,7 +6672,7 @@ mod tests { context: TransmitDataMsg { endpoint: Endpoint::Key(cryptde.public_key().clone()), last_data: false, - sequence_number_opt: None, + sequence_number: None, data: Vec::new(), }, recipient, @@ -6681,7 +6735,7 @@ mod tests { context: TransmitDataMsg { endpoint: Endpoint::Key(cryptde.public_key().clone()), last_data: false, - sequence_number_opt: None, + sequence_number: None, data: Vec::new(), }, recipient, @@ -6710,7 +6764,7 @@ mod tests { let context = TransmitDataMsg { endpoint: Endpoint::Key(cryptde.public_key().clone()), last_data: false, - sequence_number_opt: None, + sequence_number: None, data: Vec::new(), }; let context_a = context.clone(); @@ -6810,7 +6864,7 @@ mod tests { context: TransmitDataMsg { endpoint: Endpoint::Key(cryptde.public_key().clone()), last_data: false, - sequence_number_opt: None, + sequence_number: None, data: Vec::new(), }, recipient, @@ -6838,7 +6892,7 @@ mod tests { let context = TransmitDataMsg { endpoint: Endpoint::Key(cryptde.public_key().clone()), last_data: false, - sequence_number_opt: None, + sequence_number: None, data: Vec::new(), }; let context_a = context.clone(); @@ -6935,7 +6989,7 @@ mod tests { target_component: Component::ProxyClient, return_component_opt: Some(Component::ProxyServer), payload_size: 10000, - host: Host::new("booga.com", 1234), + hostname_opt: None, }); assert_eq!( @@ -6981,16 +7035,16 @@ mod tests { target_component: Component::ProxyClient, return_component_opt: Some(Component::ProxyServer), payload_size: 10000, - host: Host::new("host.name", 88), + hostname_opt: None, }); let next_door_neighbor_cryptde = CryptDENull::from(&next_door_neighbor.public_key(), TEST_DEFAULT_CHAIN); let exit_node_cryptde = CryptDENull::from(&exit_node.public_key(), TEST_DEFAULT_CHAIN); - let response = result.clone().unwrap(); - let hops = &response.route.hops; + + let hops = result.clone().unwrap().route.hops; let actual_keys: Vec = match hops.as_slice() { - [hop, exit, hop_back, origin, empty] => vec![ + [hop, exit, hop_back, origin, empty, _accounting] => vec![ decodex::(CRYPTDE_PAIR.main.as_ref(), hop) .expect("hop") .public_key, @@ -7007,11 +7061,7 @@ mod tests { .expect("empty") .public_key, ], - l => panic!( - "our match is wrong, real size is {} instead of 5, {:?}", - l.len(), - l - ), + l => panic!("our match is wrong, real size is {}, {:?}", l.len(), l), }; let expected_public_keys = vec![ next_door_neighbor.public_key().clone(), @@ -7021,38 +7071,6 @@ mod tests { PublicKey::new(b""), ]; assert_eq!(expected_public_keys, actual_keys); - assert_eq!( - response.expected_services, - RoundTrip( - vec![ - Nothing, - Routing( - next_door_neighbor.public_key().clone(), - next_door_neighbor.earning_wallet(), - next_door_neighbor.rate_pack().clone() - ), - Exit( - exit_node.public_key().clone(), - exit_node.earning_wallet(), - exit_node.rate_pack().clone() - ), - ], - vec![ - Exit( - exit_node.public_key().clone(), - exit_node.earning_wallet(), - exit_node.rate_pack().clone() - ), - Routing( - next_door_neighbor.public_key().clone(), - next_door_neighbor.earning_wallet(), - next_door_neighbor.rate_pack().clone() - ), - Nothing, - ] - ) - ); - assert_eq!(response.host, Host::new("host.name", 88)); } fn assert_route_query_message(min_hops: Hops) { @@ -7071,7 +7089,7 @@ mod tests { target_component: Component::ProxyClient, return_component_opt: Some(Component::ProxyServer), payload_size: 10000, - host: Host::new("booga.com", 1234), + hostname_opt: None, }); let assert_hops = |cryptdes: Vec, route: &[CryptData]| { @@ -7081,23 +7099,24 @@ mod tests { } }; /* - This is how the route_hops vector looks like: [C1, C2, ..., C(nodes_count), ..., C2, C1] + This is how the route_hops vector looks like: [C1, C2, ..., C(nodes_count), ..., C2, C1, accounting] Let's consider for 3-hop route ==> Nodes Count --> 4 Route Length --> 8 - Route Hops --> [C1, C2, C3, C4, C3, C2, C1] + Route Hops --> [C1, C2, C3, C4, C3, C2, C1, accounting] Over Route --> [C1, C2, C3] Back Route --> [C4, C3, C2, C1] */ - let route_hops = result.unwrap().route.hops; + let mut route_hops = result.unwrap().route.hops; let route_length = route_hops.len(); + let _accounting = route_hops.pop(); let over_route = &route_hops[..hops]; let back_route = &route_hops[hops..]; let over_cryptdes = cryptdes_from_node_records(&nodes[..hops]); let mut back_cryptdes = cryptdes_from_node_records(&nodes); back_cryptdes.reverse(); - assert_eq!(route_length, 2 * nodes_count - 1); + assert_eq!(route_length, 2 * nodes_count); assert_hops(over_cryptdes, over_route); assert_hops(back_cryptdes, back_route); } @@ -7171,16 +7190,16 @@ mod tests { target_component: Component::ProxyClient, return_component_opt: Some(Component::ProxyServer), payload_size, - host: Host::new("booga.com", 1234), + hostname_opt: None, }) .unwrap(); let (over, back) = match response.expected_services { - OneWay(_) => panic!("Expecting RoundTrip"), - RoundTrip(o, b) => (o[1].clone(), b[1].clone()), + ExpectedServices::OneWay(_) => panic!("Expecting RoundTrip"), + ExpectedServices::RoundTrip(o, b, _) => (o[1].clone(), b[1].clone()), }; let extract_key = |es: ExpectedService| match es { - Routing(pk, _, _) => pk, + ExpectedService::Routing(pk, _, _) => pk, x => panic!("Expecting Routing, found {:?}", x), }; let expected_relay_key = if a_not_b { a.clone() } else { b.clone() }; @@ -7572,6 +7591,24 @@ mod tests { subject } + fn make_o_r_e_subject() -> (NodeRecord, NodeRecord, NodeRecord, Neighborhood) { + let mut subject = make_standard_subject(); + let o = &subject.neighborhood_database.root().clone(); + let r = &make_node_record(4567, false); + let e = &make_node_record(5678, false); + { + let db = &mut subject.neighborhood_database; + db.add_node(r.clone()).unwrap(); + db.add_node(e.clone()).unwrap(); + let mut dual_edge = |a: &NodeRecord, b: &NodeRecord| { + db.add_arbitrary_full_neighbor(a.public_key(), b.public_key()) + }; + dual_edge(o, r); + dual_edge(r, e); + } + (o.clone(), r.clone(), e.clone(), subject) + } + fn segment(nodes: &[&NodeRecord], component: &Component) -> RouteSegment { RouteSegment::new( nodes.into_iter().map(|n| n.public_key()).collect(), diff --git a/node/src/proxy_client/mod.rs b/node/src/proxy_client/mod.rs index c17daaf16..aa0f33010 100644 --- a/node/src/proxy_client/mod.rs +++ b/node/src/proxy_client/mod.rs @@ -613,7 +613,7 @@ mod tests { sequence_number: 0, last_data: false, }, - target_hostname: String::from("target.hostname.com"), + target_hostname: Some(String::from("target.hostname.com")), target_port: 1234, protocol: ProxyProtocol::HTTP, originator_public_key: PublicKey::new(&b"originator_public_key"[..]), @@ -622,7 +622,7 @@ mod tests { let package = ExpiredCoresPackage::new( SocketAddr::from_str("1.2.3.4:1234").unwrap(), Some(make_wallet("consuming")), - route_to_proxy_client(&cryptde.public_key(), cryptde, false), + route_to_proxy_client(&cryptde.public_key(), cryptde), request, 0, ); @@ -754,7 +754,7 @@ mod tests { sequence_number: 0, last_data: false, }, - target_hostname: "booga.com".to_string(), + target_hostname: None, target_port: 0, protocol: ProxyProtocol::HTTP, originator_public_key: PublicKey::new(&b"originator"[..]), @@ -813,7 +813,7 @@ mod tests { sequence_number: 0, last_data: false, }, - target_hostname: "booga.com".to_string(), + target_hostname: None, target_port: 0, protocol: ProxyProtocol::HTTP, originator_public_key: PublicKey::new(&b"originator"[..]), @@ -870,7 +870,7 @@ mod tests { sequence_number: 0, last_data: false, }, - target_hostname: "booga.com".to_string(), + target_hostname: None, target_port: 0, protocol: ProxyProtocol::HTTP, originator_public_key: alias_cryptde.public_key().clone(), @@ -1226,7 +1226,7 @@ mod tests { sequence_number: 0, last_data: false, }, - target_hostname: "booga.com".to_string(), + target_hostname: None, target_port: 0, protocol: ProxyProtocol::HTTP, originator_public_key: originator_public_key.clone(), diff --git a/node/src/proxy_client/stream_establisher.rs b/node/src/proxy_client/stream_establisher.rs index a07e4d730..602b3c0c1 100644 --- a/node/src/proxy_client/stream_establisher.rs +++ b/node/src/proxy_client/stream_establisher.rs @@ -160,8 +160,6 @@ mod tests { fn spawn_stream_reader_handles_data() { let (proxy_client, proxy_client_awaiter, proxy_client_recording_arc) = make_recorder(); let (sub_tx, sub_rx) = unbounded(); - let stream_key = StreamKey::make_meaningless_stream_key(); - let stream_key_inner = stream_key.clone(); thread::spawn(move || { let system = System::new("spawn_stream_reader_handles_data"); let peer_actors = peer_actors_builder().proxy_client(proxy_client).build(); @@ -195,13 +193,13 @@ mod tests { }; subject.spawn_stream_reader( &ClientRequestPayload_0v1 { - stream_key: stream_key_inner, + stream_key: StreamKey::make_meaningless_stream_key(), sequenced_packet: SequencedPacket { data: vec![], sequence_number: 0, last_data: false, }, - target_hostname: "blah".to_string(), + target_hostname: Some("blah".to_string()), target_port: 0, protocol: ProxyProtocol::HTTP, originator_public_key: subject.cryptde.public_key().clone(), @@ -229,7 +227,7 @@ mod tests { assert_eq!( ibsd, InboundServerData { - stream_key, + stream_key: StreamKey::make_meaningless_stream_key(), last_data: false, sequence_number: 0, source: SocketAddr::from_str("1.2.3.4:5678").unwrap(), diff --git a/node/src/proxy_client/stream_handler_pool.rs b/node/src/proxy_client/stream_handler_pool.rs index 8a42ca2ae..7fc623617 100644 --- a/node/src/proxy_client/stream_handler_pool.rs +++ b/node/src/proxy_client/stream_handler_pool.rs @@ -114,7 +114,11 @@ impl StreamHandlerPoolReal { let inner_arc_1 = inner_arc.clone(); let logger = Self::make_logger_copy(&inner_arc); let data_len = payload.sequenced_packet.data.len(); - let hostname = payload.target_hostname.clone(); + let hostname = payload + .target_hostname + .as_ref() + .unwrap_or(&"".to_string()) + .to_string(); let target_port = payload.target_port; match Self::find_stream_with_key(&stream_key, &inner_arc) { Some(sender_wrapper) => { @@ -311,14 +315,27 @@ impl StreamHandlerPoolReal { "No stream to {:?} exists; resolving host", &payload.target_hostname ); - match Self::parse_ip(&payload.target_hostname) { - Ok(socket_addr) => Self::handle_ip( - payload.clone(), - socket_addr, - inner_arc, - payload.target_hostname.clone(), - ), - Err(_) => Self::lookup_dns(inner_arc, payload.target_hostname.clone(), payload.clone()), + match payload.target_hostname { + Some(ref target_hostname) => match Self::parse_ip(target_hostname) { + Ok(socket_addr) => Self::handle_ip( + payload.clone(), + socket_addr, + inner_arc, + target_hostname.to_string(), + ), + Err(_) => Self::lookup_dns(inner_arc, target_hostname.to_string(), payload.clone()), + }, + None => { + error!( + logger, + "Cannot open new stream with key {:?}: no hostname supplied", + payload.stream_key + ); + Box::new(err::< + Box + 'static>, + String, + >("No hostname provided".to_string())) + } } } @@ -715,7 +732,7 @@ mod tests { let payload = ClientRequestPayload_0v1 { stream_key, sequenced_packet: SequencedPacket::new(b"booga".to_vec(), 0, false), - target_hostname: "www.example.com".to_string(), + target_hostname: Some("www.example.com".to_string()), target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: cryptde.public_key().clone(), @@ -748,7 +765,7 @@ mod tests { sequence_number: 0, last_data: false, }, - target_hostname: "booga.com".to_string(), + target_hostname: None, target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: PublicKey::new(&b"men's souls"[..]), @@ -803,20 +820,18 @@ mod tests { init_test_logging(); let test_name = "write_failure_for_nonexistent_stream_generates_termination_message"; let cryptde = CRYPTDE_PAIR.main.as_ref(); - let stream_key = StreamKey::make_meaningless_stream_key(); - let stream_key_inner = stream_key.clone(); let (proxy_client, proxy_client_awaiter, proxy_client_recording_arc) = make_recorder(); let originator_key = PublicKey::new(&b"men's souls"[..]); let (reader_shutdown_tx, reader_shutdown_rx) = unbounded(); thread::spawn(move || { let client_request_payload = ClientRequestPayload_0v1 { - stream_key: stream_key_inner, + stream_key: StreamKey::make_meaningless_stream_key(), sequenced_packet: SequencedPacket { data: b"These are the times".to_vec(), sequence_number: 0, last_data: false, }, - target_hostname: String::from("that.try"), + target_hostname: Some(String::from("that.try")), target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: originator_key, @@ -864,7 +879,7 @@ mod tests { assert_eq!( proxy_client_recording.get_record::(0), &InboundServerData { - stream_key: stream_key.clone(), + stream_key: StreamKey::make_meaningless_stream_key(), last_data: true, sequence_number: 0, source: SocketAddr::from_str("2.3.4.5:80").unwrap(), @@ -873,8 +888,7 @@ mod tests { ); TestLogHandler::new().exists_log_containing(&format!( "DEBUG: {test_name}: A shutdown signal was sent to the StreamReader \ - for stream key {}.", - stream_key + for stream key AAAAAAAAAAAAAAAAAAAAAAAAAAA." )); } @@ -883,19 +897,17 @@ mod tests { let cryptde = CRYPTDE_PAIR.main.as_ref(); let write_parameters = Arc::new(Mutex::new(vec![])); let expected_write_parameters = write_parameters.clone(); - let stream_key = StreamKey::make_meaningless_stream_key(); - let stream_key_inner = stream_key.clone(); let (proxy_client, proxy_client_awaiter, proxy_client_recording_arc) = make_recorder(); thread::spawn(move || { let peer_actors = peer_actors_builder().proxy_client(proxy_client).build(); let client_request_payload = ClientRequestPayload_0v1 { - stream_key: stream_key_inner, + stream_key: StreamKey::make_meaningless_stream_key(), sequenced_packet: SequencedPacket { data: b"These are the times".to_vec(), sequence_number: 0, last_data: false, }, - target_hostname: String::from("3.4.5.6:80"), + target_hostname: Some(String::from("3.4.5.6:80")), target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: PublicKey::new(&b"men's souls"[..]), @@ -968,7 +980,7 @@ mod tests { assert_eq!( proxy_client_recording.get_record::(0), &InboundServerData { - stream_key, + stream_key: StreamKey::make_meaningless_stream_key(), last_data: false, sequence_number: 0, source: SocketAddr::from_str("3.4.5.6:80").unwrap(), @@ -991,7 +1003,7 @@ mod tests { sequence_number: 0, last_data: true, }, - target_hostname: String::from("3.4.5.6:80"), + target_hostname: Some(String::from("3.4.5.6:80")), target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: PublicKey::new(&b"brutal death"[..]), @@ -1052,7 +1064,7 @@ mod tests { sequence_number: 0, last_data: true, }, - target_hostname: String::from("3.4.5.6:80"), + target_hostname: Some(String::from("3.4.5.6:80")), target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: PublicKey::new(&b"brutal death"[..]), @@ -1104,19 +1116,17 @@ mod tests { let expected_lookup_ip_parameters = lookup_ip_parameters.clone(); let write_parameters = Arc::new(Mutex::new(vec![])); let expected_write_parameters = write_parameters.clone(); - let stream_key = StreamKey::make_meaningless_stream_key(); - let stream_key_inner = stream_key.clone(); let (proxy_client, proxy_client_awaiter, proxy_client_recording_arc) = make_recorder(); thread::spawn(move || { let peer_actors = peer_actors_builder().proxy_client(proxy_client).build(); let client_request_payload = ClientRequestPayload_0v1 { - stream_key: stream_key_inner, + stream_key: StreamKey::make_meaningless_stream_key(), sequenced_packet: SequencedPacket { data: b"These are the times".to_vec(), sequence_number: 0, last_data: false, }, - target_hostname: String::from("3.4.5.6"), + target_hostname: Some(String::from("3.4.5.6")), target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: PublicKey::new(&b"men's souls"[..]), @@ -1199,7 +1209,7 @@ mod tests { assert_eq!( proxy_client_recording.get_record::(0), &InboundServerData { - stream_key, + stream_key: StreamKey::make_meaningless_stream_key(), last_data: false, sequence_number: 0, source: SocketAddr::from_str("3.4.5.6:80").unwrap(), @@ -1208,6 +1218,71 @@ mod tests { ); } + #[test] + fn missing_hostname_for_nonexistent_stream_generates_log_and_termination_message() { + init_test_logging(); + let test_name = + "missing_hostname_for_nonexistent_stream_generates_log_and_termination_message"; + let cryptde = CRYPTDE_PAIR.main.as_ref(); + let (proxy_client, proxy_client_awaiter, proxy_client_recording_arc) = make_recorder(); + let originator_key = PublicKey::new(&b"men's souls"[..]); + let stream_key = StreamKey::make_meaningful_stream_key(test_name); + thread::spawn(move || { + let peer_actors = peer_actors_builder().proxy_client(proxy_client).build(); + let client_request_payload = ClientRequestPayload_0v1 { + stream_key: stream_key.clone(), + sequenced_packet: SequencedPacket { + data: b"These are the times".to_vec(), + sequence_number: 0, + last_data: false, + }, + target_hostname: None, + target_port: HTTP_PORT, + protocol: ProxyProtocol::HTTP, + originator_public_key: originator_key, + }; + let package = ExpiredCoresPackage::new( + SocketAddr::from_str("1.2.3.4:1234").unwrap(), + Some(make_wallet("consuming")), + make_meaningless_route(&CRYPTDE_PAIR), + client_request_payload.into(), + 0, + ); + let resolver = + ResolverWrapperMock::new().lookup_ip_failure(ResolveErrorKind::Io.into()); + let subject = StreamHandlerPoolReal::new( + Box::new(resolver), + cryptde, + peer_actors.accountant.report_exit_service_provided.clone(), + peer_actors.proxy_client_opt.unwrap().clone(), + 100, + 200, + ); + + run_process_package_in_actix(subject, package); + }); + + proxy_client_awaiter.await_message_count(1); + let proxy_client_recording = proxy_client_recording_arc.lock().unwrap(); + assert_eq!( + proxy_client_recording.get_record::(0), + &InboundServerData { + stream_key: stream_key.clone(), + last_data: true, + sequence_number: 0, + source: error_socket_addr(), + data: vec![], + } + ); + TestLogHandler::new().exists_log_containing( + format!( + "ERROR: ProxyClient: Cannot open new stream with key {:?}: no hostname supplied", + stream_key + ) + .as_str(), + ); + } + #[test] fn nonexistent_connection_springs_into_being_and_is_persisted_to_handle_transaction() { let cryptde = CRYPTDE_PAIR.main.as_ref(); @@ -1215,8 +1290,6 @@ mod tests { let expected_lookup_ip_parameters = lookup_ip_parameters.clone(); let write_parameters = Arc::new(Mutex::new(vec![])); let expected_write_parameters = write_parameters.clone(); - let stream_key = StreamKey::make_meaningless_stream_key(); - let stream_key_inner = stream_key.clone(); let (proxy_client, proxy_client_awaiter, proxy_client_recording_arc) = make_recorder(); let (accountant, accountant_awaiter, accountant_recording_arc) = make_recorder(); let before = SystemTime::now(); @@ -1226,13 +1299,13 @@ mod tests { .accountant(accountant) .build(); let client_request_payload = ClientRequestPayload_0v1 { - stream_key: stream_key_inner, + stream_key: StreamKey::make_meaningless_stream_key(), sequenced_packet: SequencedPacket { data: b"These are the times".to_vec(), sequence_number: 0, last_data: false, }, - target_hostname: String::from("that.try"), + target_hostname: Some(String::from("that.try")), target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: PublicKey::new(&b"men's souls"[..]), @@ -1317,7 +1390,7 @@ mod tests { assert_eq!( proxy_client_recording.get_record::(0), &InboundServerData { - stream_key, + stream_key: StreamKey::make_meaningless_stream_key(), last_data: false, sequence_number: 0, source: SocketAddr::from_str("3.4.5.6:80").unwrap(), @@ -1345,7 +1418,7 @@ mod tests { sequence_number: 0, last_data: false, }, - target_hostname: String::from("that.try"), + target_hostname: Some(String::from("that.try")), target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: originator_key, @@ -1467,7 +1540,7 @@ mod tests { sequence_number: 0, last_data: false, }, - target_hostname: String::from("blockedwebsite.com"), + target_hostname: Some(String::from("blockedwebsite.com")), target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: originator_key, @@ -1564,7 +1637,7 @@ mod tests { let client_request_payload = ClientRequestPayload_0v1 { stream_key, sequenced_packet: sequenced_packet.clone(), - target_hostname: String::from("that.try"), + target_hostname: Some(String::from("that.try")), target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: PublicKey::new(&b"men's souls"[..]), @@ -1677,7 +1750,7 @@ mod tests { sequence_number: 0, last_data: true, }, - target_hostname: String::from("that.try"), + target_hostname: Some(String::from("that.try")), target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: originator_key, @@ -1739,7 +1812,7 @@ mod tests { let client_request_payload = ClientRequestPayload_0v1 { stream_key: stream_key.clone(), sequenced_packet: sequenced_packet.clone(), - target_hostname: String::from("that.try"), + target_hostname: Some(String::from("that.try")), target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: PublicKey::new(&b"men's souls"[..]), @@ -1810,7 +1883,7 @@ mod tests { sequence_number: 0, last_data: false, }, - target_hostname: "booga.com".to_string(), + target_hostname: None, target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: PublicKey::new(&b"booga"[..]), diff --git a/node/src/proxy_client/stream_reader.rs b/node/src/proxy_client/stream_reader.rs index bf39de6e3..992b58dbf 100644 --- a/node/src/proxy_client/stream_reader.rs +++ b/node/src/proxy_client/stream_reader.rs @@ -179,10 +179,9 @@ mod tests { }); let proxy_client_sub = rx.recv().unwrap(); - let stream_key = StreamKey::make_meaningless_stream_key(); let (stream_killer, stream_killer_params) = unbounded(); let mut subject = StreamReader { - stream_key: stream_key.clone(), + stream_key: StreamKey::make_meaningless_stream_key(), proxy_client_sub, stream, stream_killer, @@ -199,7 +198,7 @@ mod tests { assert_eq!( proxy_client_recording.get_record::(0), &InboundServerData { - stream_key: stream_key.clone(), + stream_key: StreamKey::make_meaningless_stream_key(), last_data: false, sequence_number: 0, source: SocketAddr::from_str("8.7.4.3:50").unwrap(), @@ -209,7 +208,7 @@ mod tests { assert_eq!( proxy_client_recording.get_record::(1), &InboundServerData { - stream_key: stream_key.clone(), + stream_key: StreamKey::make_meaningless_stream_key(), last_data: false, sequence_number: 1, source: SocketAddr::from_str("8.7.4.3:50").unwrap(), @@ -219,7 +218,7 @@ mod tests { assert_eq!( proxy_client_recording.get_record::(2), &InboundServerData { - stream_key: stream_key.clone(), + stream_key: StreamKey::make_meaningless_stream_key(), last_data: false, sequence_number: 2, source: SocketAddr::from_str("8.7.4.3:50").unwrap(), @@ -227,7 +226,10 @@ mod tests { }, ); let stream_killer_parameters = stream_killer_params.try_recv().unwrap(); - assert_eq!(stream_killer_parameters, (stream_key, 3)); + assert_eq!( + stream_killer_parameters, + (StreamKey::make_meaningless_stream_key(), 3) + ); } #[test] @@ -264,7 +266,6 @@ mod tests { let (stream_killer, stream_killer_params) = unbounded(); let peer_addr = SocketAddr::from_str("5.7.9.0:95").unwrap(); let mut subject = make_subject(); - let stream_key = subject.stream_key.clone(); subject.proxy_client_sub = proxy_client_sub; subject.stream = Box::new(stream); subject.stream_killer = stream_killer; @@ -278,7 +279,7 @@ mod tests { assert_eq!( proxy_client_recording.get_record::(0), &InboundServerData { - stream_key: stream_key.clone(), + stream_key: StreamKey::make_meaningless_stream_key(), last_data: false, sequence_number: 0, source: peer_addr, @@ -288,7 +289,7 @@ mod tests { assert_eq!( proxy_client_recording.get_record::(1), &InboundServerData { - stream_key: stream_key.clone(), + stream_key: StreamKey::make_meaningless_stream_key(), last_data: false, sequence_number: 1, source: peer_addr, @@ -298,7 +299,7 @@ mod tests { assert_eq!( proxy_client_recording.get_record::(2), &InboundServerData { - stream_key: stream_key.clone(), + stream_key: StreamKey::make_meaningless_stream_key(), last_data: false, sequence_number: 2, source: peer_addr, @@ -309,7 +310,10 @@ mod tests { let kill_stream_msg = stream_killer_params .try_recv() .expect("stream was not killed"); - assert_eq!(kill_stream_msg, (stream_key, 3)); + assert_eq!( + kill_stream_msg, + (StreamKey::make_meaningless_stream_key(), 3) + ); assert!(stream_killer_params.try_recv().is_err()); } diff --git a/node/src/proxy_server/client_request_payload_factory.rs b/node/src/proxy_server/client_request_payload_factory.rs index 4f9e5df60..75a484a37 100644 --- a/node/src/proxy_server/client_request_payload_factory.rs +++ b/node/src/proxy_server/client_request_payload_factory.rs @@ -3,7 +3,6 @@ use crate::proxy_server::protocol_pack::from_ibcd; use crate::sub_lib::cryptde::CryptDE; use crate::sub_lib::cryptde::PlainData; use crate::sub_lib::dispatcher::InboundClientData; -use crate::sub_lib::host::Host; use crate::sub_lib::proxy_server::ClientRequestPayload_0v1; use crate::sub_lib::sequence_buffer::SequencedPacket; use crate::sub_lib::stream_key::StreamKey; @@ -14,7 +13,6 @@ pub trait ClientRequestPayloadFactory { &self, ibcd: &InboundClientData, stream_key: StreamKey, - host_opt: Option, cryptde: &dyn CryptDE, logger: &Logger, ) -> Option; @@ -28,44 +26,11 @@ impl ClientRequestPayloadFactory for ClientRequestPayloadFactoryReal { &self, ibcd: &InboundClientData, stream_key: StreamKey, - host_from_history_opt: Option, cryptde: &dyn CryptDE, logger: &Logger, ) -> Option { let protocol_pack = from_ibcd(ibcd).map_err(|e| error!(logger, "{}", e)).ok()?; - let host_from_request_result_closure = Box::new(|| { - let data = PlainData::new(&ibcd.data); - match protocol_pack.find_host(&data) { - Some(host) => Ok(host), - // So far we've only looked in the client packet; but this message will evaporate - // unless there's no host information in host_opt (from ProxyServer's StreamInfo) either. - None => Err(format!( - "No hostname information found in either client packet ({}) or ProxyServer for protocol {:?}, with StreamKey {}", - protocol_pack.describe_packet(&data), - protocol_pack.proxy_protocol(), - stream_key - )), - } - }); - let target_host = match (host_from_request_result_closure(), host_from_history_opt) { - (Ok(host), _) => host, - (Err(_), Some(host)) => host, - (Err(e), None) => { - if ibcd.last_data && ibcd.data.is_empty() { - warning!( - logger, - "Client opened {:?} connection and immediately closed it without sending any data, with StreamKey {}", - protocol_pack.proxy_protocol(), - stream_key - ); - } else { - error!(logger, "{}", e); - } - error!(logger, "{}", e); - return None; - } - }; - let sequence_number = match ibcd.sequence_number_opt { + let sequence_number = match ibcd.sequence_number { Some(sequence_number) => sequence_number, None => { error!( @@ -76,6 +41,12 @@ impl ClientRequestPayloadFactory for ClientRequestPayloadFactoryReal { return None; } }; + let data = PlainData::new(&ibcd.data); + let target_host = protocol_pack.find_host(&data); + let (target_hostname_opt, target_port) = match target_host { + Some(host) => (Some(host.name), host.port), + None => (None, protocol_pack.standard_port()), + }; Some(ClientRequestPayload_0v1 { stream_key, sequenced_packet: SequencedPacket { @@ -83,8 +54,8 @@ impl ClientRequestPayloadFactory for ClientRequestPayloadFactoryReal { sequence_number, last_data: ibcd.last_data, }, - target_hostname: target_host.name, - target_port: target_host.port, + target_hostname: target_hostname_opt, + target_port, protocol: protocol_pack.proxy_protocol(), originator_public_key: cryptde.public_key().clone(), }) @@ -103,7 +74,7 @@ mod tests { use crate::bootstrapper::CryptDEPair; use crate::sub_lib::proxy_server::ProxyProtocol; use lazy_static::lazy_static; - use masq_lib::constants::{HTTP_PORT, TLS_PORT}; + use masq_lib::constants::HTTP_PORT; use masq_lib::test_utils::logging::init_test_logging; use masq_lib::test_utils::logging::TestLogHandler; use std::net::SocketAddr; @@ -114,127 +85,14 @@ mod tests { static ref CRYPTDE_PAIR: CryptDEPair = CryptDEPair::null(); } - #[test] - fn ibcd_hostname_overrides_supplied_hostname() { - let data = PlainData::new(&b"GET http://borkoed.com:1234/fleebs.html HTTP/1.1\r\n\r\n"[..]); - let ibcd = InboundClientData { - timestamp: SystemTime::now(), - client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port_opt: Some(HTTP_PORT), - sequence_number_opt: Some(1), - last_data: false, - is_clandestine: false, - data: data.clone().into(), - }; - let cryptde = CRYPTDE_PAIR.main.dup(); - let stream_key = StreamKey::make_meaningless_stream_key(); - let logger = Logger::new("ibcd_hostname_overrides_supplied_hostname"); - let subject = Box::new(ClientRequestPayloadFactoryReal::new()); - - let result = subject - .make( - &ibcd, - stream_key, - Some(Host::new("ignored.com", 4321)), - cryptde.as_ref(), - &logger, - ) - .unwrap(); - - assert_eq!(result.target_hostname, String::from("borkoed.com")); - assert_eq!(result.target_port, 1234); - } - - #[test] - fn uses_supplied_host_if_ibcd_does_not_have_one() { - let test_name = "uses_supplied_hostname_if_ibcd_does_not_have_one"; - let data = PlainData::new(&[0x01, 0x02, 0x03]); // No host can be extracted here - let ibcd = InboundClientData { - timestamp: SystemTime::now(), - client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port_opt: Some(HTTP_PORT), - sequence_number_opt: Some(1), - last_data: false, - is_clandestine: false, - data: data.into(), - }; - let cryptde = CRYPTDE_PAIR.main.dup(); - let stream_key = StreamKey::make_meaningful_stream_key(test_name); - let logger = Logger::new(test_name); - let subject = Box::new(ClientRequestPayloadFactoryReal::new()); - let supplied_host = Host::new("supplied.com", 4321); - - let result = subject - .make( - &ibcd, - stream_key, - Some(supplied_host.clone()), - cryptde.as_ref(), - &logger, - ) - .unwrap(); - - assert_eq!(result.target_hostname, supplied_host.name); - } - - #[test] - fn logs_error_and_returns_none_if_no_ibcd_host_and_no_supplied_host() { - init_test_logging(); - let test_name = "logs_error_and_returns_none_if_no_ibcd_hostname_and_no_supplied_hostname"; - let data = PlainData::new(&[0x01, 0x02, 0x03]); // no host can be extracted here - let ibcd = InboundClientData { - timestamp: SystemTime::now(), - client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port_opt: Some(HTTP_PORT), - sequence_number_opt: Some(1), - last_data: false, - is_clandestine: false, - data: data.into(), - }; - let cryptde = CRYPTDE_PAIR.main.dup(); - let stream_key = StreamKey::make_meaningful_stream_key(test_name); - let logger = Logger::new(test_name); - let subject = Box::new(ClientRequestPayloadFactoryReal::new()); - - let result = subject.make(&ibcd, stream_key, None, cryptde.as_ref(), &logger); - - assert_eq!(result, None); - TestLogHandler::new().exists_log_containing(&format!("ERROR: {test_name}: No hostname information found in either client packet (Malformed HTTP request: '') or ProxyServer for protocol HTTP")); - } - - #[test] - fn logs_different_error_and_returns_none_if_connection_is_opened_and_immediately_closed() { - init_test_logging(); - let test_name = - "logs_different_error_and_returns_none_if_connection_is_opened_and_immediately_closed"; - let ibcd = InboundClientData { - timestamp: SystemTime::now(), - client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port_opt: Some(HTTP_PORT), - sequence_number_opt: Some(1), - last_data: true, - is_clandestine: false, - data: vec![], - }; - let cryptde = CRYPTDE_PAIR.main.dup(); - let stream_key = StreamKey::make_meaningful_stream_key(test_name); - let logger = Logger::new(test_name); - let subject = Box::new(ClientRequestPayloadFactoryReal::new()); - - let result = subject.make(&ibcd, stream_key, None, cryptde.as_ref(), &logger); - - assert_eq!(result, None); - TestLogHandler::new().exists_log_containing(&format!("WARN: {test_name}: Client opened HTTP connection and immediately closed it without sending any data")); - } - #[test] fn handles_http_with_a_port() { let data = PlainData::new(&b"GET http://borkoed.com:2345/fleebs.html HTTP/1.1\r\n\r\n"[..]); let ibcd = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port_opt: Some(HTTP_PORT), - sequence_number_opt: Some(1), + reception_port: Some(HTTP_PORT), + sequence_number: Some(1), last_data: false, is_clandestine: false, data: data.clone().into(), @@ -244,7 +102,7 @@ mod tests { let logger = Logger::new("test"); let subject = Box::new(ClientRequestPayloadFactoryReal::new()); - let result = subject.make(&ibcd, stream_key, None, cryptde, &logger); + let result = subject.make(&ibcd, stream_key, cryptde, &logger); assert_eq!( result, @@ -255,7 +113,7 @@ mod tests { sequence_number: 1, last_data: false }, - target_hostname: String::from("borkoed.com"), + target_hostname: Some(String::from("borkoed.com")), target_port: 2345, protocol: ProxyProtocol::HTTP, originator_public_key: cryptde.public_key().clone(), @@ -270,8 +128,8 @@ mod tests { let ibcd = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port_opt: Some(HTTP_PORT), - sequence_number_opt: Some(1), + reception_port: Some(HTTP_PORT), + sequence_number: Some(1), last_data: false, is_clandestine: false, data: data.clone().into(), @@ -281,7 +139,7 @@ mod tests { let stream_key = StreamKey::make_meaningful_stream_key(test_name); let subject = Box::new(ClientRequestPayloadFactoryReal::new()); - let result = subject.make(&ibcd, stream_key, None, cryptde, &logger); + let result = subject.make(&ibcd, stream_key, cryptde, &logger); assert_eq!( result, @@ -292,7 +150,7 @@ mod tests { sequence_number: 1, last_data: false }, - target_hostname: String::from("borkoed.com"), + target_hostname: Some(String::from("borkoed.com")), target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: cryptde.public_key().clone(), @@ -325,8 +183,8 @@ mod tests { let ibcd = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - sequence_number_opt: Some(0), - reception_port_opt: Some(443), + sequence_number: Some(0), + reception_port: Some(443), last_data: false, is_clandestine: false, data: data.clone().into(), @@ -336,7 +194,7 @@ mod tests { let logger = Logger::new("test"); let subject = Box::new(ClientRequestPayloadFactoryReal::new()); - let result = subject.make(&ibcd, stream_key, None, cryptde, &logger); + let result = subject.make(&ibcd, stream_key, cryptde, &logger); assert_eq!( result, @@ -347,8 +205,8 @@ mod tests { sequence_number: 0, last_data: false }, - target_hostname: String::from("server.com"), - target_port: TLS_PORT, + target_hostname: Some(String::from("server.com")), + target_port: 443, protocol: ProxyProtocol::TLS, originator_public_key: cryptde.public_key().clone(), }) @@ -357,7 +215,6 @@ mod tests { #[test] fn handles_tls_without_hostname() { - init_test_logging(); let test_name = "handles_tls_without_hostname"; let data = PlainData::new(&[ 0x16, // content_type: Handshake @@ -376,10 +233,10 @@ mod tests { let ibcd = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port_opt: Some(443), + reception_port: Some(443), last_data: true, is_clandestine: false, - sequence_number_opt: Some(0), + sequence_number: Some(0), data: data.clone().into(), }; let cryptde = CRYPTDE_PAIR.main.as_ref(); @@ -387,10 +244,23 @@ mod tests { let stream_key = StreamKey::make_meaningful_stream_key(test_name); let subject = Box::new(ClientRequestPayloadFactoryReal::new()); - let result = subject.make(&ibcd, stream_key, None, cryptde, &logger); + let result = subject.make(&ibcd, stream_key, cryptde, &logger); - assert_eq!(result, None); - TestLogHandler::new().exists_log_containing(&format!("ERROR: {test_name}: No hostname information found in either client packet (ClientHello with no SNI extension) or ProxyServer for protocol TLS")); + assert_eq!( + result, + Some(ClientRequestPayload_0v1 { + stream_key, + sequenced_packet: SequencedPacket { + data: data.into(), + sequence_number: 0, + last_data: true + }, + target_hostname: None, + target_port: 443, + protocol: ProxyProtocol::TLS, + originator_public_key: cryptde.public_key().clone(), + }) + ); } #[test] @@ -400,8 +270,8 @@ mod tests { let ibcd = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - sequence_number_opt: Some(0), - reception_port_opt: None, + sequence_number: Some(0), + reception_port: None, last_data: false, is_clandestine: false, data: vec![0x10, 0x11, 0x12], @@ -411,7 +281,7 @@ mod tests { let stream_key = StreamKey::make_meaningful_stream_key(test_name); let subject = Box::new(ClientRequestPayloadFactoryReal::new()); - let result = subject.make(&ibcd, stream_key, None, cryptde, &logger); + let result = subject.make(&ibcd, stream_key, cryptde, &logger); assert_eq!(result, None); TestLogHandler::new().exists_log_containing( @@ -426,8 +296,8 @@ mod tests { let ibcd = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port_opt: Some(1234), - sequence_number_opt: Some(0), + reception_port: Some(1234), + sequence_number: Some(0), last_data: false, is_clandestine: true, data: vec![0x10, 0x11, 0x12], @@ -437,7 +307,7 @@ mod tests { let stream_key = StreamKey::make_meaningful_stream_key(test_name); let subject = Box::new(ClientRequestPayloadFactoryReal::new()); - let result = subject.make(&ibcd, stream_key, None, cryptde, &logger); + let result = subject.make(&ibcd, stream_key, cryptde, &logger); assert_eq!(result, None); TestLogHandler::new().exists_log_containing(&format!("ERROR: {test_name}: No protocol associated with origin port 1234 for 3-byte non-clandestine packet: [16, 17, 18]")); @@ -445,14 +315,13 @@ mod tests { #[test] fn use_sequence_from_inbound_client_data_in_client_request_payload() { - let data = PlainData::new(&b"GET http://borkoed.com/fleebs.html HTTP/1.1\r\n\r\n"[..]); let ibcd = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:80").unwrap(), - reception_port_opt: Some(HTTP_PORT), - sequence_number_opt: Some(1), + reception_port: Some(HTTP_PORT), + sequence_number: Some(1), last_data: false, - data: data.into(), + data: vec![0x10, 0x11, 0x12], is_clandestine: false, }; let cryptde = CRYPTDE_PAIR.main.as_ref(); @@ -463,7 +332,6 @@ mod tests { .make( &ibcd, StreamKey::make_meaningless_stream_key(), - None, cryptde, &logger, ) @@ -476,26 +344,25 @@ mod tests { fn makes_no_payload_if_sequence_number_is_unknown() { init_test_logging(); let test_name = "makes_no_payload_if_sequence_number_is_unknown"; - let data = PlainData::new(&b"GET http://borkoed.com/fleebs.html HTTP/1.1\r\n\r\n"[..]); let ibcd = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:80").unwrap(), - reception_port_opt: Some(HTTP_PORT), + reception_port: Some(HTTP_PORT), last_data: false, is_clandestine: false, - sequence_number_opt: None, - data: data.into(), + sequence_number: None, + data: vec![1, 3, 5, 7], }; let cryptde = CRYPTDE_PAIR.main.as_ref(); let logger = Logger::new(test_name); let stream_key = StreamKey::make_meaningful_stream_key(test_name); let subject = Box::new(ClientRequestPayloadFactoryReal::new()); - let result = subject.make(&ibcd, stream_key, None, cryptde, &logger); + let result = subject.make(&ibcd, stream_key, cryptde, &logger); assert_eq!(result, None); TestLogHandler::new().exists_log_containing(&format!( - "ERROR: {test_name}: internal error: got IBCD with no sequence number and 47 bytes" + "ERROR: {test_name}: internal error: got IBCD with no sequence number and 4 bytes" )); } } diff --git a/node/src/proxy_server/http_protocol_pack.rs b/node/src/proxy_server/http_protocol_pack.rs index ef8940c6d..b611f2be3 100644 --- a/node/src/proxy_server/http_protocol_pack.rs +++ b/node/src/proxy_server/http_protocol_pack.rs @@ -1,8 +1,7 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::proxy_server::protocol_pack::{ProtocolPack, ServerImpersonator}; +use crate::proxy_server::protocol_pack::{Host, ProtocolPack, ServerImpersonator}; use crate::proxy_server::server_impersonator_http::ServerImpersonatorHttp; use crate::sub_lib::cryptde::PlainData; -use crate::sub_lib::host::Host; use crate::sub_lib::proxy_server::ProxyProtocol; use lazy_static::lazy_static; use masq_lib::constants::HTTP_PORT; @@ -13,7 +12,6 @@ lazy_static! { static ref HOST_PATTERN: Regex = Regex::new(r"^(?:https?://)?([^\s/]+)").expect("bad regex"); } -#[derive(Clone, Copy)] pub struct HttpProtocolPack {} impl ProtocolPack for HttpProtocolPack { @@ -35,14 +33,6 @@ impl ProtocolPack for HttpProtocolPack { fn server_impersonator(&self) -> Box { Box::new(ServerImpersonatorHttp {}) } - - fn describe_packet(&self, data: &PlainData) -> String { - if data.as_slice().starts_with(b"HTTP/") { - self.describe_response(data) - } else { - self.describe_request(data) - } - } } impl HttpProtocolPack { @@ -110,82 +100,6 @@ impl HttpProtocolPack { Ok(port) => Ok(port), } } - - fn describe_request(&self, data: &PlainData) -> String { - let first_line_end = data - .as_slice() - .iter() - .position(|&b| b == b'\r') - .unwrap_or(data.len()); - let first_line = &data.as_slice()[0..first_line_end]; - let mut parts = first_line.split(|&b| b == b' '); - if let (Some(method_bytes), Some(path_bytes), Some(http_version_bytes)) = - (parts.next(), parts.next(), parts.next()) - { - let method = Self::from_utf8(method_bytes); - let path = Self::from_utf8(path_bytes); - let http_version = Self::from_utf8(http_version_bytes); - if let Some(host) = self.find_host(data) { - return if path.starts_with('/') { - format!( - "{} {} request to {}{}", - http_version, method, host.name, path - ) - } else { - format!("{} {} request to {}", http_version, method, path) - }; - } else { - return format!( - "{} {} request to unknown host: {}", - http_version, method, path - ); - } - } - format!( - "Malformed HTTP request: {}", - Self::truncate_data_as_string(data, 50) - ) - } - - fn describe_response(&self, data: &PlainData) -> String { - let first_line_end = data - .as_slice() - .iter() - .position(|&b| b == b'\r') - .unwrap_or(data.as_slice().len()); - let first_line = &data.as_slice()[0..first_line_end]; - let mut parts = first_line.split(|&b| b == b' '); - if let (Some(http_version_bytes), Some(status_code_bytes), Some(status_text_bytes)) = - (parts.next(), parts.next(), parts.next()) - { - let http_with_version = Self::from_utf8(http_version_bytes); - let status_code = Self::from_utf8(status_code_bytes); - let status_text = Self::from_utf8(status_text_bytes); - return format!( - "{} response with status {} {}", - http_with_version, status_code, status_text - ); - } - format!( - "Malformed HTTP response: {}", - Self::truncate_data_as_string(data, 50) - ) - } - - fn truncate_data_as_string(data: &PlainData, truncate_at: usize) -> String { - if data.as_slice().len() > truncate_at { - format!( - "'{}'...", - String::from_utf8_lossy(&data.as_slice()[0..truncate_at]) - ) - } else { - format!("'{}'", String::from_utf8_lossy(data.as_slice())) - } - } - - fn from_utf8(bytes: &[u8]) -> String { - String::from_utf8_lossy(bytes).to_string() - } } #[cfg(test)] @@ -422,83 +336,4 @@ mod tests { let data = b"CONNECTX"; assert!(!HttpProtocolPack::is_connect(data)); } - - #[test] - fn describe_packet_works_on_get_request_with_header_host() { - let data = - PlainData::new(b"GET /index.html?item=booga HTTP/1.1\r\nHost: www.example.com\r\n\r\n"); - let subject = HttpProtocolPack {}; - - let result = subject.describe_packet(&data); - - assert_eq!( - result, - "HTTP/1.1 GET request to www.example.com/index.html?item=booga" - ); - } - - #[test] - fn describe_packet_works_on_post_request_with_url_host() { - let data = PlainData::new( - b"POST www.example.com/person/1234 HTTP/1.1\r\nContent-Length: 2\r\n\r\n{}", - ); - let subject = HttpProtocolPack {}; - - let result = subject.describe_packet(&data); - - assert_eq!( - result, - "HTTP/1.1 POST request to www.example.com/person/1234" - ); - } - - #[test] - fn describe_packet_works_on_unexpected_request_with_unspecified_host() { - let data = PlainData::new(b"BOOGA /person/1234 HTTP/1.1\r\nContent-Length: 2\r\n\r\n{}"); - let subject = HttpProtocolPack {}; - - let result = subject.describe_packet(&data); - - assert_eq!( - result, - "HTTP/1.1 BOOGA request to unknown host: /person/1234" - ); - } - - #[test] - fn describe_packet_works_on_malformed_request() { - let data = PlainData::new(b"Fourscore_and_seven_years_ago_our_fathers_brought_forth_on_this_continent_a_new_nation,_conceived_in_liberty_and_dedicated_to_the_proposition_that_all_men_are_created_equal."); - let subject = HttpProtocolPack {}; - - let result = subject.describe_packet(&data); - - assert_eq!( - result, - "Malformed HTTP request: 'Fourscore_and_seven_years_ago_our_fathers_brought_'..." - ); - } - - #[test] - fn describe_packet_works_on_200_response() { - let data = - PlainData::new(b"HTTP/1.1 200 OK\r\nContent-Type: text/html\r\n\r\n"); - let subject = HttpProtocolPack {}; - - let result = subject.describe_packet(&data); - - assert_eq!(result, "HTTP/1.1 response with status 200 OK"); - } - - #[test] - fn describe_packet_works_on_malformed_response() { - let data = PlainData::new(b"HTTP/Fourscore_and_seven_years_ago_our_fathers_brought_forth_on_this_continent_a_new_nation,_conceived_in_liberty_and_dedicated_to_the_proposition_that_all_men_are_created_equal."); - let subject = HttpProtocolPack {}; - - let result = subject.describe_packet(&data); - - assert_eq!( - result, - "Malformed HTTP response: 'HTTP/Fourscore_and_seven_years_ago_our_fathers_bro'..." - ); - } } diff --git a/node/src/proxy_server/mod.rs b/node/src/proxy_server/mod.rs index 68d7b1ebc..459acf668 100644 --- a/node/src/proxy_server/mod.rs +++ b/node/src/proxy_server/mod.rs @@ -24,7 +24,6 @@ use crate::sub_lib::cryptde::PublicKey; use crate::sub_lib::dispatcher::InboundClientData; use crate::sub_lib::dispatcher::{Endpoint, StreamShutdownMsg}; use crate::sub_lib::hopper::{ExpiredCoresPackage, IncipientCoresPackage}; -use crate::sub_lib::host::Host; use crate::sub_lib::neighborhood::RouteQueryResponse; use crate::sub_lib::neighborhood::{ExpectedService, UpdateNodeRecordMetadataMessage}; use crate::sub_lib::neighborhood::{ExpectedServices, RatePack}; @@ -32,13 +31,14 @@ use crate::sub_lib::neighborhood::{NRMetadataChange, RouteQueryMessage}; use crate::sub_lib::peer_actors::BindMessage; use crate::sub_lib::proxy_client::{ClientResponsePayload_0v1, DnsResolveFailure_0v1}; use crate::sub_lib::proxy_server::ProxyServerSubs; -use crate::sub_lib::proxy_server::StreamKeyPurge; +use crate::sub_lib::proxy_server::{AddReturnRouteMessage, StreamKeyPurge}; use crate::sub_lib::proxy_server::{ AddRouteResultMessage, ClientRequestPayload_0v1, ProxyProtocol, }; use crate::sub_lib::route::Route; use crate::sub_lib::stream_handler_pool::TransmitDataMsg; use crate::sub_lib::stream_key::StreamKey; +use crate::sub_lib::ttl_hashmap::TtlHashMap; use crate::sub_lib::utils::{handle_ui_crash_request, MessageScheduler, NODE_MAILBOX_CAPACITY}; use crate::sub_lib::wallet::Wallet; use actix::Context; @@ -53,13 +53,15 @@ use masq_lib::utils::MutabilityConflictHelper; use regex::Regex; use std::collections::HashMap; use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr}; +use std::rc::Rc; use std::str::FromStr; use std::time::{Duration, SystemTime}; use tokio::prelude::Future; pub const CRASH_KEY: &str = "PROXYSERVER"; +pub const RETURN_ROUTE_TTL: Duration = Duration::from_secs(120); + pub const STREAM_KEY_PURGE_DELAY: Duration = Duration::from_secs(30); -pub const DNS_FAILURE_RETRIES: usize = 3; struct ProxyServerOutSubs { dispatcher: Recipient, @@ -67,31 +69,27 @@ struct ProxyServerOutSubs { accountant: Recipient, route_source: Recipient, update_node_record_metadata: Recipient, + add_return_route: Recipient, stream_shutdown_sub: Recipient, route_result_sub: Recipient, schedule_stream_key_purge: Recipient>, } -#[derive(Clone, Debug)] -struct StreamInfo { - tunneled_host_opt: Option, - dns_failure_retry_opt: Option, - route_opt: Option, - protocol_opt: Option, - time_to_live_opt: Option, -} - pub struct ProxyServer { subs: Option, client_request_payload_factory: Box, stream_key_factory: Box, keys_and_addrs: BidiHashMap, - stream_info: HashMap, + tunneled_hosts: HashMap, + dns_failure_retries: HashMap, + stream_key_routes: HashMap, + stream_key_ttl: HashMap, is_decentralized: bool, consuming_wallet_balance: Option, cryptde_pair: CryptDEPair, crashable: bool, logger: Logger, + route_ids_to_return_routes: TtlHashMap, browser_proxy_sequence_offset: bool, inbound_client_data_helper_opt: Option>, stream_key_purge_delay: Duration, @@ -113,6 +111,7 @@ impl Handler for ProxyServer { 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, stream_shutdown_sub: msg.peer_actors.proxy_server.stream_shutdown_sub, route_result_sub: msg.peer_actors.proxy_server.route_result_sub, schedule_stream_key_purge: msg.peer_actors.proxy_server.schedule_stream_key_purge, @@ -129,19 +128,64 @@ impl Handler for ProxyServer { self.tls_connect(&msg); self.browser_proxy_sequence_offset = true; } else if let Err(e) = - // NOTE: I removed a 'false' parameter here for retire_stream_key because I think it was wrong. - self.help(|helper, proxy| helper.handle_normal_client_data(proxy, msg)) + self.help(|helper, proxy| helper.handle_normal_client_data(proxy, msg, false)) { error!(self.logger, "{}", e) } } } +impl Handler for ProxyServer { + type Result = (); + + fn handle(&mut self, msg: AddReturnRouteMessage, _ctx: &mut Self::Context) -> Self::Result { + self.route_ids_to_return_routes + .insert(msg.return_route_id, msg); + } +} + +impl AddReturnRouteMessage { + pub fn find_exit_node_key(&self) -> Option<&PublicKey> { + self.expected_services + .iter() + .find_map(|service| match service { + ExpectedService::Exit(public_key, _, _) => Some(public_key), + _ => None, + }) + } + + pub fn is_zero_hop(&self) -> bool { + self.expected_services == vec![ExpectedService::Nothing, ExpectedService::Nothing] + } +} + impl Handler for ProxyServer { type Result = (); fn handle(&mut self, msg: AddRouteResultMessage, _ctx: &mut Self::Context) -> Self::Result { - self.handle_add_route_result_message(msg) + let dns_failure = self + .dns_failure_retries + .get(&msg.stream_key) + .unwrap_or_else(|| { + panic!("AddRouteResultMessage Handler: stream key: {} not found within dns_failure_retries", msg.stream_key); + }); + + match msg.result { + Ok(route_query_response) => { + debug!( + self.logger, + "Found a new route for hostname: {:?} - stream key: {} retries left: {}", + dns_failure.unsuccessful_request.target_hostname, + msg.stream_key, + dns_failure.retries_left + ); + self.stream_key_routes + .insert(msg.stream_key, route_query_response); + } + Err(e) => { + warning!(self.logger, "No route found for hostname: {:?} - stream key {} - retries left: {} - AddRouteResultMessage Error: {}",dns_failure.unsuccessful_request.target_hostname, msg.stream_key, dns_failure.retries_left, e); + } + } } } @@ -212,18 +256,21 @@ impl ProxyServer { crashable: bool, is_running_in_integration_test: bool, ) -> ProxyServer { - let ps_logger = Logger::new("ProxyServer"); ProxyServer { subs: None, client_request_payload_factory: Box::new(ClientRequestPayloadFactoryReal::new()), stream_key_factory: Box::new(StreamKeyFactoryReal {}), keys_and_addrs: BidiHashMap::new(), - stream_info: HashMap::new(), + tunneled_hosts: HashMap::new(), + dns_failure_retries: HashMap::new(), + stream_key_routes: HashMap::new(), + stream_key_ttl: HashMap::new(), is_decentralized, consuming_wallet_balance, cryptde_pair, crashable, - logger: ps_logger, + 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::new())), stream_key_purge_delay: STREAM_KEY_PURGE_DELAY, @@ -237,6 +284,7 @@ impl ProxyServer { from_dispatcher: recipient!(addr, InboundClientData), from_hopper: recipient!(addr, ExpiredCoresPackage), dns_failure_from_hopper: recipient!(addr, ExpiredCoresPackage), + add_return_route: recipient!(addr, AddReturnRouteMessage), stream_shutdown_sub: recipient!(addr, StreamShutdownMsg), node_from_ui: recipient!(addr, NodeFromUiMessage), route_result_sub: recipient!(addr, AddRouteResultMessage), @@ -244,46 +292,24 @@ impl ProxyServer { } } - fn stream_info(&self, stream_key: &StreamKey) -> Option<&StreamInfo> { - match self.stream_info.get(stream_key) { - None => { - error!( - self.logger, - "Stream key {} not found in stream_info", stream_key - ); - None - } - Some(info) => Some(info), - } - } - - fn stream_info_mut(&mut self, stream_key: &StreamKey) -> Option<&mut StreamInfo> { - match self.stream_info.get_mut(stream_key) { - None => { - error!( - self.logger, - "Stream key {} not found in stream_info", stream_key - ); - None - } - Some(info) => Some(info), - } - } - fn remove_dns_failure_retry( - stream_info: &mut StreamInfo, + &mut self, stream_key: &StreamKey, ) -> Result { - match stream_info.dns_failure_retry_opt.take() { + match self.dns_failure_retries.remove(stream_key) { None => Err(format!( - "No DNSFailureRetry entry found for the stream_key: {:?}", + "No entry found inside dns_failure_retries hashmap for the stream_key: {:?}", stream_key )), Some(retry) => Ok(retry), } } - fn retry_dns_resolution(&mut self, retry: &DNSFailureRetry, client_addr: SocketAddr) { + fn retry_dns_resolution( + &mut self, + retry: DNSFailureRetry, + client_addr: SocketAddr, + ) -> DNSFailureRetry { let args = TransmitToHopperArgs::new( self, retry.unsuccessful_request.clone(), @@ -291,6 +317,7 @@ impl ProxyServer { SystemTime::now(), false, ); + let add_return_route_sub = self.out_subs("ProxyServer").add_return_route.clone(); let route_source = self.out_subs("Neighborhood").route_source.clone(); let proxy_server_sub = self.out_subs("ProxyServer").route_result_sub.clone(); let inbound_client_data_helper = self @@ -298,7 +325,13 @@ impl ProxyServer { .as_ref() .expect("IBCDHelper uninitialized"); - inbound_client_data_helper.request_route_and_transmit(args, route_source, proxy_server_sub); + inbound_client_data_helper.request_route_and_transmit( + args, + add_return_route_sub, + route_source, + proxy_server_sub, + ); + retry } fn retire_stream_key(&mut self, stream_key: &StreamKey) { @@ -309,7 +342,7 @@ impl ProxyServer { &self, client_addr: SocketAddr, proxy_protocol: ProxyProtocol, - hostname: String, + hostname_opt: Option, ) { self.subs .as_ref() @@ -318,261 +351,119 @@ impl ProxyServer { .try_send(TransmitDataMsg { endpoint: Endpoint::Socket(client_addr), last_data: true, - sequence_number_opt: Some(0), // DNS resolution errors always happen on the first request + sequence_number: Some(0), // DNS resolution errors always happen on the first request data: from_protocol(proxy_protocol) .server_impersonator() - .dns_resolution_failure_response(hostname), + .dns_resolution_failure_response(hostname_opt), }) .expect("Dispatcher is dead"); } - fn get_response_services( - route_query_response: &RouteQueryResponse, - ) -> Option<&[ExpectedService]> { - match &route_query_response.expected_services { - ExpectedServices::RoundTrip(_, back) => Some(back), - _ => None, - } - } - - fn find_exit_node_key(response_services: &[ExpectedService]) -> Option { - response_services - .iter() - .find_map(|service| service.exit_node_key_opt()) - } - - fn handle_add_route_result_message(&mut self, msg: AddRouteResultMessage) { - // We can't access self.logger for logging once we obtain mutable access to a stream_info - // element. So we create a delayed_log closure that we can call with self.logger after - // we've finished with the mutable borrow. We have to use #[allow(unused_assignments)] - // because Rust can't figure out that delayed_log will always be assigned before it's used. - type DelayedLogArgs = Box; - #[allow(unused_assignments)] - let mut delayed_log: DelayedLogArgs = Box::new(|_, _, _, _, _| {}); - let (target_hostname, stream_key, retries_left, message) = { - let mut stream_info = self.stream_info_mut(&msg.stream_key).unwrap_or_else(|| { - panic!( - "AddRouteResultMessage Handler: stream key: {} not found", - msg.stream_key - ) - }); - let dns_failure_retry = stream_info - .dns_failure_retry_opt - .as_ref() - .unwrap_or_else(|| - panic!("AddRouteResultMessage Handler: dns_failure_retry_opt is None for stream key {}", msg.stream_key) - ); - let mut message = String::new(); - match msg.result { - Ok(route_query_response) => { - delayed_log = Box::new( - move |logger: &Logger, - target_hostname: String, - stream_key: StreamKey, - retries_left: usize, - _: String| { - debug!( - logger, - "Found a new route for hostname: {:?} - stream key: {} retries left: {}", - target_hostname, - stream_key, - retries_left - ); - }, - ); - stream_info.route_opt = Some(route_query_response); - } - Err(e) => { - message = e; - delayed_log = Box::new( - move |logger: &Logger, - target_hostname: String, - stream_key: StreamKey, - retries_left: usize, - message: String| { - warning!( - logger, - "No route found for hostname: {:?} - stream key {} - retries left: {} - AddRouteResultMessage Error: {}", - target_hostname, - stream_key, - retries_left, - message - ); - }, - ); - } - } - ( - dns_failure_retry - .unsuccessful_request - .target_hostname - .clone(), - msg.stream_key, - dns_failure_retry.retries_left, - message, - ) - }; - delayed_log( - &self.logger, - target_hostname, - stream_key, - retries_left, - message, - ); - } - fn handle_dns_resolve_failure(&mut self, msg: &ExpiredCoresPackage) { - let response = &msg.payload; - - // The idea here is that the Borrow Checker will not allow us to modify the StreamInfo in - // ProxyServer's map while we're looking at one of its values. So we're making a mutable - // copy of the StreamInfo, modifying that as necessary, and then overwriting the original - // map element with the modified copy at the end of the function. However, under certain - // circumstances we want to _retire_ the stream key; so we have a restore_stream_info - // flag that starts out true and is set to false if we retire the stream key. It's an - // ugly hack. Thanks, Borrow Checker! - let mut stream_info = match self.stream_info.remove(&response.stream_key) { - Some(info) => info, - None => { - error!( - self.logger, - "Discarding DnsResolveFailure message from an unrecognized stream key {:?}", - &response.stream_key - ); - return; - } + let return_route_info = + match self.get_return_route_info(&msg.remaining_route, "dns resolve failure") { + Some(rri) => rri, + None => return, // TODO: Eventually we'll have to do something better here, but we'll probably need some heuristics. + }; + let exit_public_key = { + // ugly, ugly + let self_public_key = self.cryptde_pair.main.as_ref().public_key(); + return_route_info + .find_exit_node_key() + .unwrap_or_else(|| { + if !self.is_decentralized { + self_public_key + } else { + panic!( + "Internal error: return_route_info for {} has no exit Node", + return_route_info.return_route_id + ); + } + }) + .clone() }; - let mut restore_stream_info = true; - let route_query_response = match &stream_info.route_opt { - Some(route_query_response) => route_query_response, - None => { - error!( - self.logger, - "Stream info for stream key {} has no route info", &response.stream_key - ); - return; - } - }; - let response_services = match Self::get_response_services(route_query_response) { - Some(response_services) => response_services, - None => { - error!( - self.logger, - "Stream info for stream key {} has no response services in its route info", - &response.stream_key - ); - return; - } - }; - let exit_public_key = if !self.is_decentralized { - self.cryptde_pair.main.public_key().clone() - } else { - match Self::find_exit_node_key(response_services) { - Some(exit_public_key) => exit_public_key, - None => { - error!( - self.logger, - "Stream info for stream key {} has no exit node in its response services", - &response.stream_key - ); - return; - } - } - }; + let hostname_opt = return_route_info.hostname_opt.clone(); let response = &msg.payload; match self.keys_and_addrs.a_to_b(&response.stream_key) { Some(client_addr) => { - self.subs - .as_ref() - .expect("Neighborhood unbound in ProxyServer") - .update_node_record_metadata - .try_send(UpdateNodeRecordMetadataMessage { - public_key: exit_public_key, - metadata_change: NRMetadataChange::AddUnreachableHost { - hostname: route_query_response.host.name.clone(), - }, - }) - .expect("Neighborhood is dead"); - self.report_response_services_consumed(response_services, 0, msg.payload_len); - if let Some(retry_ref) = &mut stream_info.dns_failure_retry_opt { - debug!( + if let Some(server_name) = hostname_opt.clone() { + self.subs + .as_ref() + .expect("Neighborhood unbound in ProxyServer") + .update_node_record_metadata + .try_send(UpdateNodeRecordMetadataMessage { + public_key: exit_public_key, + metadata_change: NRMetadataChange::AddUnreachableHost { + hostname: server_name, + }, + }) + .expect("Neighborhood is dead"); + } else { + error!( self.logger, - "Handling DNS failure for hostname {:?} - stream key: {} retries left: {}", - retry_ref.unsuccessful_request.target_hostname, - &response.stream_key, - retry_ref.retries_left + "Exit node {exit_public_key} complained of DNS failure, but was given no hostname to resolve." ); - if retry_ref.retries_left > 0 { - self.retry_dns_resolution(retry_ref, client_addr); - retry_ref.retries_left -= 1; - } else { - restore_stream_info = false; - self.retire_stream_key(&response.stream_key); - let protocol = stream_info.protocol_opt.expect( - "StreamInfo should always have a protocol_opt set by the time we get a DNS failure" - ); - self.send_dns_failure_response_to_the_browser( - client_addr, - protocol, - route_query_response.host.name.clone(), + // TODO: Malefactor ban the exit node because it lied about the DNS failure. + } + self.report_response_services_consumed(&return_route_info, 0, msg.payload_len); + let retry = match self.remove_dns_failure_retry(&response.stream_key) { + Ok(retry) => retry, + Err(error_msg) => { + error!( + self.logger, + "While handling ExpiredCoresPackage: {}", error_msg ); + return; } + }; + if retry.retries_left > 0 { + let mut returned_retry = self.retry_dns_resolution(retry, client_addr); + returned_retry.retries_left -= 1; + self.dns_failure_retries + .insert(response.stream_key, returned_retry); } else { - error!( - self.logger, - "While handling ExpiredCoresPackage: No DNSFailureRetry entry found for the stream_key: {:?}", - &response.stream_key + self.retire_stream_key(&response.stream_key); + self.send_dns_failure_response_to_the_browser( + client_addr, + return_route_info.protocol, + hostname_opt, ); - return; } } None => { error!(self.logger, "Discarding DnsResolveFailure message for {} from an unrecognized stream key {:?}", - route_query_response.host.name, + hostname_opt.unwrap_or_else(|| "".to_string()), &response.stream_key ) } } - if restore_stream_info { - self.stream_info.insert(response.stream_key, stream_info); - } } fn schedule_stream_key_purge(&mut self, stream_key: StreamKey) { - let stream_key_purge_delay = self.stream_key_purge_delay; - // We can't access self.logger for logging once we obtain mutable access to a stream_info - // element. So we create a delayed_log closure that we can call with self.logger after - // we've finished with the mutable borrow. - let mut delayed_log: Box = Box::new(|_: &Logger| {}); - if let Some(stream_info) = self.stream_info_mut(&stream_key) { - let host_info = match &stream_info.tunneled_host_opt { - None => String::from(""), - Some(hostname) => format!(", which was tunneling to the host {:?}", hostname), - }; - delayed_log = Box::new(move |logger: &Logger| { - debug!( - logger, - "Client closed stream referenced by stream key {:?}{}. It will be purged after {:?}.", - &stream_key, - host_info, - stream_key_purge_delay - ); - }); - stream_info.time_to_live_opt = Some(SystemTime::now()); - self.subs - .as_ref() - .expect("ProxyServer Subs Unbound") - .schedule_stream_key_purge - .try_send(MessageScheduler { - scheduled_msg: StreamKeyPurge { stream_key }, - delay: self.stream_key_purge_delay, - }) - .expect("ProxyServer is dead"); - } - delayed_log(&self.logger); + let host_info = match self.tunneled_hosts.get(&stream_key) { + None => String::from(""), + Some(hostname) => format!(", which was tunneling to the host {:?}", hostname), + }; + debug!( + self.logger, + "Client closed stream referenced by stream key {:?}{}. It will be purged after {:?}.", + &stream_key, + host_info, + self.stream_key_purge_delay + ); + self.stream_key_ttl.insert(stream_key, SystemTime::now()); + self.subs + .as_ref() + .expect("ProxyServer Subs Unbound") + .schedule_stream_key_purge + .try_send(MessageScheduler { + scheduled_msg: StreamKeyPurge { stream_key }, + delay: self.stream_key_purge_delay, + }) + .expect("ProxyServer is dead"); } fn log_straggling_packet( @@ -597,6 +488,14 @@ impl ProxyServer { &mut self, msg: ExpiredCoresPackage, ) { + debug!( + self.logger, + "ExpiredCoresPackage remaining_route: {}", + msg.remaining_route.to_string(vec![ + self.cryptde_pair.main.as_ref(), + self.cryptde_pair.main.as_ref() + ]) + ); let payload_data_len = msg.payload_len; let response = msg.payload; debug!( @@ -604,70 +503,68 @@ impl ProxyServer { "Relaying ClientResponsePayload (stream key {}, sequence {}, length {}) from Hopper to Dispatcher for client", response.stream_key, response.sequenced_packet.sequence_number, response.sequenced_packet.data.len() ); - let expected_services = match self.get_expected_return_services(&response.stream_key) { - Some(expected_services) => expected_services, - None => return, - }; + let return_route_info = + match self.get_return_route_info(&msg.remaining_route, "client response") { + Some(rri) => rri, + None => return, + }; self.report_response_services_consumed( - &expected_services, + &return_route_info, response.sequenced_packet.data.len(), payload_data_len, ); let stream_key = response.stream_key; - if let Some(info) = self.stream_info_mut(&stream_key) { - if let Err(e) = ProxyServer::remove_dns_failure_retry(info, &stream_key) { + match self.remove_dns_failure_retry(&stream_key) { + Ok(_) => { + debug!(self.logger, "Successful attempt of DNS resolution, removing DNS retry entry for stream key: {}", &response.stream_key) + } + Err(_) => { trace!( self.logger, - "No DNS retry entry found for stream key {} during a successful attempt: {}", - &stream_key, - e - ); + "No DNS retry entry found for stream key: {} during a successful attempt", + &response.stream_key + ) } } - if let Some(info) = self.stream_info(&stream_key) { - if let Some(old_timestamp) = info.time_to_live_opt { - self.log_straggling_packet(&stream_key, payload_data_len, &old_timestamp) - } else { - match self.keys_and_addrs.a_to_b(&stream_key) { - Some(socket_addr) => { - let last_data = response.sequenced_packet.last_data; - let sequence_number_opt = Some( - response.sequenced_packet.sequence_number - + self.browser_proxy_sequence_offset as u64, - ); - self.subs - .as_ref() - .expect("Dispatcher unbound in ProxyServer") - .dispatcher - .try_send(TransmitDataMsg { - endpoint: Endpoint::Socket(socket_addr), - last_data, - sequence_number_opt, - data: response.sequenced_packet.data, - }) - .expect("Dispatcher is dead"); - if last_data { - self.purge_stream_key( - &stream_key, - "last data received from the exit node", - ); - } - } - None => { - // TODO GH-608: It would be really nice to be able to send an InboundClientData with last_data: true - // back to the ProxyClient (and the distant server) so that the server could shut down - // its stream, since the browser has shut down _its_ stream and no more data will - // ever be accepted from the server on that stream; but we don't have enough information - // to do so, since our stream key has been purged and all the information it keyed - // is gone. Sorry, server! - warning!(self.logger, - "Discarding {}-byte packet {} from an unrecognized stream key: {:?}; can't send response back to client", - response.sequenced_packet.data.len(), - response.sequenced_packet.sequence_number, - response.stream_key, - ) + if let Some(old_timestamp) = self.stream_key_ttl.get(&stream_key) { + self.log_straggling_packet(&stream_key, payload_data_len, old_timestamp) + } else { + match self.keys_and_addrs.a_to_b(&stream_key) { + Some(socket_addr) => { + let last_data = response.sequenced_packet.last_data; + let sequence_number = Some( + response.sequenced_packet.sequence_number + + self.browser_proxy_sequence_offset as u64, + ); + self.subs + .as_ref() + .expect("Dispatcher unbound in ProxyServer") + .dispatcher + .try_send(TransmitDataMsg { + endpoint: Endpoint::Socket(socket_addr), + last_data, + sequence_number, + data: response.sequenced_packet.data, + }) + .expect("Dispatcher is dead"); + if last_data { + self.purge_stream_key(&stream_key, "last data received from the exit node"); } } + None => { + // TODO GH-608: It would be really nice to be able to send an InboundClientData with last_data: true + // back to the ProxyClient (and the distant server) so that the server could shut down + // its stream, since the browser has shut down _its_ stream and no more data will + // ever be accepted from the server on that stream; but we don't have enough information + // to do so, since our stream key has been purged and all the information it keyed + // is gone. Sorry, server! + warning!(self.logger, + "Discarding {}-byte packet {} from an unrecognized stream key: {:?}; can't send response back to client", + response.sequenced_packet.data.len(), + response.sequenced_packet.sequence_number, + response.stream_key, + ) + } } } } @@ -677,10 +574,7 @@ impl ProxyServer { match http_data { Some(ref host) if host.port == TLS_PORT => { let stream_key = self.find_or_generate_stream_key(msg); - match self.stream_info_mut(&stream_key) { - None => return, - Some(stream_info) => stream_info.tunneled_host_opt = Some(host.name.clone()), - } + self.tunneled_hosts.insert(stream_key, host.name.clone()); self.subs .as_ref() .expect("Dispatcher unbound in ProxyServer") @@ -688,7 +582,7 @@ impl ProxyServer { .try_send(TransmitDataMsg { endpoint: Endpoint::Socket(msg.client_addr), last_data: false, - sequence_number_opt: msg.sequence_number_opt, + sequence_number: msg.sequence_number, data: b"HTTP/1.1 200 OK\r\n\r\n".to_vec(), }) .expect("Dispatcher is dead"); @@ -701,7 +595,7 @@ impl ProxyServer { .try_send(TransmitDataMsg { endpoint: Endpoint::Socket(msg.client_addr), last_data: true, - sequence_number_opt: msg.sequence_number_opt, + sequence_number: msg.sequence_number, data: b"HTTP/1.1 400 Bad Request\r\nContent-Length: 0\r\n\r\n".to_vec(), }) .expect("Dispatcher is dead"); @@ -742,13 +636,14 @@ impl ProxyServer { let ibcd = InboundClientData { timestamp: SystemTime::now(), client_addr: msg.peer_addr, - reception_port_opt: Some(nca.reception_port), + reception_port: Some(nca.reception_port), last_data: true, is_clandestine: false, - sequence_number_opt: Some(nca.sequence_number), + sequence_number: Some(nca.sequence_number), data: vec![], }; - if let Err(e) = self.help(|helper, proxy| helper.handle_normal_client_data(proxy, ibcd)) + if let Err(e) = + self.help(|helper, proxy| helper.handle_normal_client_data(proxy, ibcd, true)) { error!(self.logger, "{}", e) }; @@ -772,16 +667,6 @@ impl ProxyServer { ibcd.client_addr, ); self.keys_and_addrs.insert(stream_key, ibcd.client_addr); - self.stream_info.insert( - stream_key, - StreamInfo { - tunneled_host_opt: None, - dns_failure_retry_opt: None, - route_opt: None, - protocol_opt: None, - time_to_live_opt: None, - }, - ); debug!( self.logger, "find_or_generate_stream_key() inserted new key {} for {}", @@ -799,7 +684,9 @@ impl ProxyServer { "Retiring stream key {} due to {}", &stream_key, reason ); let _ = self.keys_and_addrs.remove_a(stream_key); - let _ = self.stream_info.remove(stream_key); + let _ = self.stream_key_routes.remove(stream_key); + let _ = self.tunneled_hosts.remove(stream_key); + let _ = self.stream_key_ttl.remove(stream_key); } fn make_payload( @@ -807,17 +694,10 @@ impl ProxyServer { ibcd: InboundClientData, stream_key: &StreamKey, ) -> Result { - let stream_info_opt = self.stream_info.get(stream_key); - let (host_opt, tunnelled_host_opt) = match stream_info_opt { - None => (None, None), - Some(info) => match &info.route_opt { - Some(route) => (Some(route.host.clone()), info.tunneled_host_opt.clone()), - None => (None, info.tunneled_host_opt.clone()), - }, - }; - let new_ibcd = match tunnelled_host_opt { + let tunnelled_host = self.tunneled_hosts.get(stream_key); + let new_ibcd = match tunnelled_host { Some(_) => InboundClientData { - reception_port_opt: Some(TLS_PORT), + reception_port: Some(443), ..ibcd }, None => ibcd, @@ -825,14 +705,13 @@ impl ProxyServer { match self.client_request_payload_factory.make( &new_ibcd, *stream_key, - host_opt, self.cryptde_pair.alias.as_ref(), &self.logger, ) { None => Err("Couldn't create ClientRequestPayload".to_string()), - Some(payload) => match tunnelled_host_opt { + Some(payload) => match tunnelled_host { Some(hostname) => Ok(ClientRequestPayload_0v1 { - target_hostname: hostname, + target_hostname: Some(hostname.clone()), ..payload }), None => Ok(payload), @@ -842,10 +721,24 @@ impl ProxyServer { fn try_transmit_to_hopper( args: TransmitToHopperArgs, + add_return_route_sub: Recipient, route_query_response: RouteQueryResponse, ) -> Result<(), String> { match route_query_response.expected_services { - ExpectedServices::RoundTrip(over, _) => { + ExpectedServices::RoundTrip(over, back, return_route_id) => { + let return_route_info = AddReturnRouteMessage { + return_route_id, + expected_services: back, + protocol: args.payload.protocol, + hostname_opt: args.payload.target_hostname.clone(), + }; + debug!( + args.logger, + "Adding expectant return route info: {:?}", return_route_info + ); + add_return_route_sub + .try_send(return_route_info) + .expect("ProxyServer is dead"); ProxyServer::transmit_to_hopper(args, route_query_response.route, over) } _ => panic!("Expected RoundTrip ExpectedServices but got OneWay"), @@ -998,7 +891,7 @@ impl ProxyServer { source_addr: SocketAddr, dispatcher: &Recipient, ) -> String { - let target_hostname = payload.target_hostname.clone(); + let target_hostname = ProxyServer::hostname(&payload); let stream_key = payload.stream_key; ProxyServer::send_route_failure(payload, source_addr, dispatcher); format!( @@ -1014,45 +907,56 @@ impl ProxyServer { ) { let data = from_protocol(payload.protocol) .server_impersonator() - .route_query_failure_response(&payload.target_hostname); + .route_query_failure_response(&ProxyServer::hostname(&payload)); let msg = TransmitDataMsg { endpoint: Endpoint::Socket(source_addr), last_data: true, - sequence_number_opt: Some(0), + sequence_number: Some(0), data, }; dispatcher.try_send(msg).expect("Dispatcher is dead"); } - fn get_expected_return_services( - &mut self, - stream_key: &StreamKey, - ) -> Option> { - match self.stream_info(stream_key) { + fn hostname(payload: &ClientRequestPayload_0v1) -> String { + match payload.target_hostname { + Some(ref thn) => thn.clone(), + None => "".to_string(), + } + } + + fn get_return_route_info( + &self, + remaining_route: &Route, + source: &str, + ) -> Option> { + let mut mut_remaining_route = remaining_route.clone(); + mut_remaining_route + .shift(self.cryptde_pair.main.as_ref()) + .expect("Internal error: remaining route in ProxyServer with no hops"); + let return_route_id = match mut_remaining_route.id(self.cryptde_pair.main.as_ref()) { + Ok(rri) => rri, + Err(e) => { + error!(self.logger, "Can't report services consumed: {}", e); + return None; + } + }; + match self.route_ids_to_return_routes.get(&return_route_id) { + Some(rri) => Some(rri), None => { - error!(self.logger, "Can't pay for return services consumed: received response with unrecognized stream key {:?}. Ignoring", stream_key); + error!(self.logger, "Can't report services consumed: received response with bogus return-route ID {} for {}. Ignoring", return_route_id, source); None } - Some(stream_info) => { - let route = stream_info - .route_opt - .as_ref() - .unwrap_or_else(|| panic!("Internal error: Request was sent over stream {:?} without an associated route being stored in stream_info: can't pay", stream_key)); - match route.expected_services { - ExpectedServices::RoundTrip(_, ref return_services) => Some(return_services.clone()), - _ => panic!("Internal error: ExpectedServices in ProxyServer for stream key {:?} is not RoundTrip", stream_key), - } - } } } fn report_response_services_consumed( &self, - expected_services: &[ExpectedService], + return_route_info: &AddReturnRouteMessage, exit_size: usize, routing_size: usize, ) { - let exit_service_report: ExitServiceSearch = expected_services + let exit_service_report: ExitServiceSearch = return_route_info + .expected_services .iter() .filter(|service| !matches!(service, ExpectedService::Nothing)) .fold(ZeroHop, |acc, service| { @@ -1078,7 +982,8 @@ impl ProxyServer { ZeroHop => return, Definite(report) => report, }; - let routing_service_reports = expected_services + let routing_service_reports = return_route_info + .expected_services .iter() .flat_map(|service| match service { ExpectedService::Routing(_, wallet, rate_pack) => Some(RoutingServiceConsumed { @@ -1117,11 +1022,13 @@ pub trait IBCDHelper { &self, proxy_s: &mut ProxyServer, msg: InboundClientData, + retire_stream_key: bool, ) -> Result<(), String>; fn request_route_and_transmit( &self, args: TransmitToHopperArgs, + add_return_route_sub: Recipient, route_source: Recipient, proxy_server_sub: Recipient, ); @@ -1131,7 +1038,7 @@ trait RouteQueryResponseResolver: Send { fn resolve_message( &self, args: TransmitToHopperArgs, - // add_return_route_sub: Recipient, + add_return_route_sub: Recipient, proxy_server_sub: Recipient, route_result_opt: Result, MailboxError>, ); @@ -1142,13 +1049,18 @@ impl RouteQueryResponseResolver for RouteQueryResponseResolverReal { fn resolve_message( &self, args: TransmitToHopperArgs, + add_return_route_sub: Recipient, proxy_server_sub: Recipient, route_result_opt: Result, MailboxError>, ) { let stream_key = args.payload.stream_key; let result = match route_result_opt { Ok(Some(route_query_response)) => { - match ProxyServer::try_transmit_to_hopper(args, route_query_response.clone()) { + match ProxyServer::try_transmit_to_hopper( + args, + add_return_route_sub, + route_query_response.clone(), + ) { Ok(()) => Ok(route_query_response), Err(e) => Err(e), } @@ -1190,16 +1102,15 @@ impl IBCDHelperReal { } } } - impl IBCDHelper for IBCDHelperReal { fn handle_normal_client_data( &self, - proxy_server: &mut ProxyServer, + proxy: &mut ProxyServer, msg: InboundClientData, + retire_stream_key: bool, ) -> Result<(), String> { let client_addr = msg.client_addr; - let last_data = msg.last_data; - if proxy_server.consuming_wallet_balance.is_none() && proxy_server.is_decentralized { + 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, @@ -1210,25 +1121,25 @@ impl IBCDHelper for IBCDHelperReal { let msg = TransmitDataMsg { endpoint: Endpoint::Socket(client_addr), last_data: true, - sequence_number_opt: Some(0), + sequence_number: Some(0), data, }; - proxy_server + 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_server.find_or_generate_stream_key(&msg); + let stream_key = proxy.find_or_generate_stream_key(&msg); let timestamp = msg.timestamp; - let payload = match proxy_server.make_payload(msg, &stream_key) { + let payload = match proxy.make_payload(msg, &stream_key) { Ok(payload) => { - if !proxy_server.is_running_in_integration_test { - if let Err(e) = - Hostname::new(&payload.target_hostname).validate_non_loopback_host() - { - return Err(format!("Request to wildcard IP detected - {} (Most likely because Blockchain Service URL is not set)", e)); + if !proxy.is_running_in_integration_test { + if let Some(hostname) = &payload.target_hostname { + if let Err(e) = Hostname::new(hostname).validate_non_loopback_host() { + return Err(format!("Request to wildcard IP detected - {} (Most likely because Blockchain Service URL is not set)", e)); + } } } payload @@ -1236,47 +1147,38 @@ impl IBCDHelper for IBCDHelperReal { Err(e) => return Err(e), }; - { - let is_decentralized = proxy_server.is_decentralized; - let mut stream_info = proxy_server - .stream_info_mut(&stream_key) - .unwrap_or_else(|| panic!("Stream key {} disappeared!", &stream_key)); - if stream_info.dns_failure_retry_opt.is_none() { - let dns_failure_retry = DNSFailureRetry { - unsuccessful_request: payload.clone(), - retries_left: if is_decentralized { - DNS_FAILURE_RETRIES - } else { - 0 - }, - }; - stream_info.dns_failure_retry_opt = Some(dns_failure_retry); - stream_info.protocol_opt = Some(payload.protocol); - } + if proxy.dns_failure_retries.get(&stream_key).is_none() { + let dns_failure_retry = DNSFailureRetry { + unsuccessful_request: payload.clone(), + retries_left: if proxy.is_decentralized { 3 } else { 0 }, + }; + proxy + .dns_failure_retries + .insert(stream_key, dns_failure_retry); } let args = - TransmitToHopperArgs::new(proxy_server, payload, client_addr, timestamp, last_data); + TransmitToHopperArgs::new(proxy, payload, client_addr, timestamp, retire_stream_key); + let add_return_route_sub = proxy.out_subs("ProxysServer").add_return_route.clone(); let pld = &args.payload; - let stream_info = proxy_server - .stream_info(&pld.stream_key) - .unwrap_or_else(|| panic!("Stream key {} disappeared!", &pld.stream_key)); - if let Some(route_query_response) = &stream_info.route_opt { + if let Some(route_query_response) = proxy.stream_key_routes.get(&pld.stream_key) { debug!( - proxy_server.logger, + 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(args, route_query_response) + ProxyServer::try_transmit_to_hopper(args, add_return_route_sub, route_query_response) } else { - let route_source = proxy_server.out_subs("Neighborhood").route_source.clone(); - let proxy_server_sub = proxy_server - .out_subs("ProxyServer") - .route_result_sub - .clone(); - self.request_route_and_transmit(args, route_source, proxy_server_sub); + let route_source = proxy.out_subs("Neighborhood").route_source.clone(); + let proxy_server_sub = proxy.out_subs("ProxyServer").route_result_sub.clone(); + self.request_route_and_transmit( + args, + add_return_route_sub, + route_source, + proxy_server_sub, + ); Ok(()) } } @@ -1284,11 +1186,12 @@ impl IBCDHelper for IBCDHelperReal { fn request_route_and_transmit( &self, args: TransmitToHopperArgs, + add_return_route_sub: Recipient, neighborhood_sub: Recipient, proxy_server_sub: Recipient, ) { let pld = &args.payload; - let host = Host::new(&pld.target_hostname, pld.target_port); + let hostname_opt = pld.target_hostname.clone(); let logger = args.logger.clone(); debug!( logger, @@ -1303,11 +1206,16 @@ impl IBCDHelper for IBCDHelperReal { tokio::spawn( neighborhood_sub .send(RouteQueryMessage::data_indefinite_route_request( - host, + hostname_opt, payload_size, )) .then(move |route_result| { - message_resolver.resolve_message(args, proxy_server_sub, route_result); + message_resolver.resolve_message( + args, + add_return_route_sub, + proxy_server_sub, + route_result, + ); Ok(()) }), ); @@ -1446,7 +1354,6 @@ impl Hostname { #[cfg(test)] mod tests { use super::*; - use crate::blockchain::bip32::Bip32EncryptionKeyProvider; use crate::bootstrapper::CryptDEPair; use crate::match_every_type_id; use crate::proxy_server::protocol_pack::ServerImpersonator; @@ -1460,7 +1367,6 @@ mod tests { use crate::sub_lib::dispatcher::Component; use crate::sub_lib::hop::LiveHop; use crate::sub_lib::hopper::MessageType; - use crate::sub_lib::host::Host; 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}; @@ -1469,6 +1375,7 @@ mod tests { use crate::sub_lib::route::Route; use crate::sub_lib::route::RouteSegment; use crate::sub_lib::sequence_buffer::SequencedPacket; + use crate::sub_lib::ttl_hashmap::TtlHashMap; use crate::sub_lib::versioned_data::VersionedData; use crate::test_utils::make_meaningless_route; use crate::test_utils::make_paying_wallet; @@ -1483,6 +1390,7 @@ mod tests { }; use crate::test_utils::zero_hop_route_response; use actix::System; + use crossbeam_channel::unbounded; use lazy_static::lazy_static; use masq_lib::constants::{HTTP_PORT, TLS_PORT}; use masq_lib::test_utils::logging::init_test_logging; @@ -1551,6 +1459,7 @@ mod tests { fn resolve_message( &self, args: TransmitToHopperArgs, + _add_return_route_sub: Recipient, _proxy_server_sub: Recipient, route_result: Result, MailboxError>, ) { @@ -1577,59 +1486,11 @@ mod tests { self } } - - struct StreamInfoBuilder { - product: StreamInfo, - } - - impl StreamInfoBuilder { - pub fn new() -> Self { - Self { - product: StreamInfo { - tunneled_host_opt: None, - dns_failure_retry_opt: None, - route_opt: None, - protocol_opt: None, - time_to_live_opt: None, - }, - } - } - - pub fn tunneled_host(mut self, host: &str) -> Self { - self.product.tunneled_host_opt = Some(host.to_string()); - self - } - - pub fn dns_failure_retry(mut self, retry: DNSFailureRetry) -> Self { - self.product.dns_failure_retry_opt = Some(retry); - self - } - - pub fn route(mut self, route: RouteQueryResponse) -> Self { - self.product.route_opt = Some(route); - self - } - - pub fn protocol(mut self, protocol: ProxyProtocol) -> Self { - self.product.protocol_opt = Some(protocol); - self - } - - pub fn time_to_live(mut self, ttl: SystemTime) -> Self { - self.product.time_to_live_opt = Some(ttl); - self - } - - pub fn build(self) -> StreamInfo { - self.product - } - } - #[test] fn constants_have_correct_values() { assert_eq!(CRASH_KEY, "PROXYSERVER"); + assert_eq!(RETURN_ROUTE_TTL, Duration::from_secs(120)); assert_eq!(STREAM_KEY_PURGE_DELAY, Duration::from_secs(30)); - assert_eq!(DNS_FAILURE_RETRIES, 3); } const STANDARD_CONSUMING_WALLET_BALANCE: i64 = 0; @@ -1643,6 +1504,7 @@ mod tests { accountant: recipient!(addr, ReportServicesConsumedMessage), route_source: recipient!(addr, RouteQueryMessage), update_node_record_metadata: recipient!(addr, UpdateNodeRecordMetadataMessage), + add_return_route: recipient!(addr, AddReturnRouteMessage), stream_shutdown_sub: recipient!(addr, StreamShutdownMsg), route_result_sub: recipient!(addr, AddRouteResultMessage), schedule_stream_key_purge: recipient!(addr, MessageScheduler), @@ -1686,9 +1548,16 @@ mod tests { } } - fn return_route(cryptde: &dyn CryptDE) -> Route { + fn return_route_with_id(cryptde: &dyn CryptDE, return_route_id: u32) -> Route { + let cover_hop = make_cover_hop(cryptde); + let id_hop = cryptde + .encode( + &cryptde.public_key(), + &PlainData::from(serde_cbor::ser::to_vec(&return_route_id).unwrap()), + ) + .unwrap(); Route { - hops: vec![make_cover_hop(cryptde)], + hops: vec![cover_hop, id_hop], } } @@ -1707,7 +1576,7 @@ mod tests { #[derive(Default)] struct IBCDHelperMock { - handle_normal_client_data_params: Arc>>, + handle_normal_client_data_params: Arc>>, handle_normal_client_data_results: RefCell>>, } @@ -1716,11 +1585,12 @@ mod tests { &self, _proxy_s: &mut ProxyServer, msg: InboundClientData, + retire_stream_key: bool, ) -> Result<(), String> { self.handle_normal_client_data_params .lock() .unwrap() - .push(msg); + .push((msg, retire_stream_key)); self.handle_normal_client_data_results .borrow_mut() .remove(0) @@ -1729,6 +1599,7 @@ mod tests { fn request_route_and_transmit( &self, _args: TransmitToHopperArgs, + _add_return_route_sub: Recipient, _route_source: Recipient, _proxy_server_sub: Recipient, ) { @@ -1739,7 +1610,7 @@ mod tests { impl IBCDHelperMock { fn handle_normal_client_data_params( mut self, - params: &Arc>>, + params: &Arc>>, ) -> Self { self.handle_normal_client_data_params = params.clone(); self @@ -1754,111 +1625,33 @@ mod tests { } #[test] - fn get_expected_services_produces_nothing_if_nothing_exists() { - let mut subject = ProxyServer::new( - CRYPTDE_PAIR.clone(), - true, - Some(STANDARD_CONSUMING_WALLET_BALANCE), - false, - false, - ); - let stream_key = StreamKey::make_meaningless_stream_key(); - - let result = subject.get_expected_return_services(&stream_key); - - assert!( - result.is_none(), - "Expected no expected services, but got: {:?}", - result - ); - } - - #[test] - fn get_expected_services_produces_rri_when_it_exists() { - let mut subject = ProxyServer::new( - CRYPTDE_PAIR.clone(), - true, - Some(STANDARD_CONSUMING_WALLET_BALANCE), - false, - false, - ); - let exit_public_key = PublicKey::new(&b"exit key"[..]); - let stream_key = StreamKey::make_meaningless_stream_key(); - let back_services = vec![ExpectedService::Exit( - exit_public_key, - make_wallet("booga"), - rate_pack(1000), - )]; - let expected_services = ExpectedServices::RoundTrip(vec![], back_services.clone()); - subject.stream_info.insert( - stream_key.clone(), - StreamInfoBuilder::new() - .route(RouteQueryResponse { - route: make_meaningless_route(&CRYPTDE_PAIR), - expected_services: expected_services.clone(), - host: Host::new("booga.com", TLS_PORT), - }) - .protocol(ProxyProtocol::TLS) - .build(), - ); - - let result = subject.get_expected_return_services(&stream_key).unwrap(); - - assert_eq!(result, back_services); - } - - #[test] - #[should_panic( - expected = "Internal error: Request was sent over stream Y29uc3RhbnQgZm9yIHBhbmljIG0 without an associated route being stored in stream_info: can't pay" - )] - fn get_expected_services_panics_if_stream_info_exists_but_has_no_route() { - let mut subject = ProxyServer::new( - CRYPTDE_PAIR.clone(), - true, - Some(STANDARD_CONSUMING_WALLET_BALANCE), - false, - false, - ); - let stream_key = StreamKey::from_bytes(b"constant for panic message"); - subject.stream_info.insert( - stream_key.clone(), - StreamInfoBuilder::new() - // no route_opt: problem - .protocol(ProxyProtocol::TLS) - .build(), - ); - - let _ = subject.get_expected_return_services(&stream_key).unwrap(); - } - - #[test] - fn proxy_server_receives_http_request_with_new_stream_key_from_dispatcher_then_sends_cores_package_to_hopper( - ) { - init_test_logging(); - let test_name = "proxy_server_receives_http_request_with_new_stream_key_from_dispatcher_then_sends_cores_package_to_hopper"; - let main_cryptde = CRYPTDE_PAIR.main.as_ref(); - let alias_cryptde = CRYPTDE_PAIR.alias.as_ref(); - 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 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![], - ), - host: Host::new("booga.com", HTTP_PORT), - })); - let (proxy_server_mock, _, proxy_server_recording_arc) = make_recorder(); - let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); + fn proxy_server_receives_http_request_with_new_stream_key_from_dispatcher_then_sends_cores_package_to_hopper( + ) { + init_test_logging(); + let test_name = "proxy_server_receives_http_request_with_new_stream_key_from_dispatcher_then_sends_cores_package_to_hopper"; + let main_cryptde = CRYPTDE_PAIR.main.as_ref(); + let alias_cryptde = CRYPTDE_PAIR.alias.as_ref(); + 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 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 = StreamKey::make_meaningless_stream_key(); let expected_data = http_request.to_vec(); let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr, - reception_port_opt: Some(HTTP_PORT), - sequence_number_opt: Some(0), + reception_port: Some(HTTP_PORT), + sequence_number: Some(0), last_data: true, is_clandestine: false, data: expected_data.clone(), @@ -1872,7 +1665,7 @@ mod tests { sequence_number: 0, last_data: true, }, - target_hostname: String::from("nowhere.com"), + target_hostname: Some(String::from("nowhere.com")), target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: alias_cryptde.public_key().clone(), @@ -1889,7 +1682,7 @@ mod tests { thread::spawn(move || { let stream_key_factory = StreamKeyFactoryMock::new() .make_parameters(&make_parameters_arc) - .make_result(stream_key.clone()); + .make_result(stream_key); let system = System::new(test_name); let mut subject = ProxyServer::new( CRYPTDE_PAIR.clone(), @@ -1911,13 +1704,6 @@ mod tests { subject_addr.try_send(msg_from_dispatcher).unwrap(); - subject_addr - .try_send(AssertionsMessage { - assertions: Box::new(move |proxy_server: &mut ProxyServer| { - assert!(proxy_server.stream_info.contains_key(&stream_key)); - }), - }) - .unwrap(); system.run(); }); @@ -1934,16 +1720,13 @@ mod tests { let record = recording.get_record::(0); assert_eq!( record, - &RouteQueryMessage::data_indefinite_route_request( - Host::new("nowhere.com", HTTP_PORT), - 47 - ) + &RouteQueryMessage::data_indefinite_route_request(Some("nowhere.com".to_string()), 47) ); let recording = proxy_server_recording_arc.lock().unwrap(); assert_eq!(recording.len(), 0); TestLogHandler::new().exists_log_containing( - &format!("DEBUG: {test_name}: Found a new route for hostname: \"nowhere.com\" - stream key: {stream_key} retries left: 3") + &format!("DEBUG: {test_name}: Found a new route for hostname: Some(\"nowhere.com\") - stream key: {stream_key} retries left: 3") ); } @@ -1960,20 +1743,19 @@ mod tests { expected_services: ExpectedServices::RoundTrip( vec![make_exit_service_from_key(destination_key.clone())], vec![], + 1234, ), - host: Host::new("booga.com", HTTP_PORT), })); 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 = StreamKey::make_meaningless_stream_key(); let request_data = http_request.to_vec(); - let tunneled_data = make_server_com_client_hello(); let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr.clone(), - reception_port_opt: Some(8443), - sequence_number_opt: Some(0), + reception_port: Some(8443), + sequence_number: Some(0), last_data: false, is_clandestine: false, data: request_data.clone(), @@ -1981,27 +1763,27 @@ mod tests { let tunnelled_msg = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr, - reception_port_opt: Some(8443), - sequence_number_opt: Some(0), + reception_port: Some(8443), + sequence_number: Some(0), last_data: false, is_clandestine: false, - data: tunneled_data.clone(), + data: b"client hello".to_vec(), }; let expected_tdm = TransmitDataMsg { endpoint: Endpoint::Socket(socket_addr), last_data: false, - sequence_number_opt: Some(0), + 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 { - data: tunneled_data, + data: b"client hello".to_vec(), sequence_number: 0, last_data: false, }, - target_hostname: String::from("realdomain.nu"), - target_port: TLS_PORT, + target_hostname: Some(String::from("realdomain.nu")), + target_port: 443, protocol: ProxyProtocol::TLS, originator_public_key: alias_cryptde.public_key().clone(), }; @@ -2018,7 +1800,7 @@ mod tests { thread::spawn(move || { let stream_key_factory = StreamKeyFactoryMock::new() .make_parameters(&make_parameters_arc_thread) - .make_result(stream_key.clone()); + .make_result(stream_key); let system = System::new( "proxy_server_receives_connect_responds_with_ok_and_stores_stream_key_and_hostname", ); @@ -2040,13 +1822,6 @@ mod tests { subject_addr.try_send(msg_from_dispatcher).unwrap(); subject_addr.try_send(tunnelled_msg).unwrap(); - subject_addr - .try_send(AssertionsMessage { - assertions: Box::new(move |proxy_server: &mut ProxyServer| { - assert!(proxy_server.stream_info.contains_key(&stream_key)); - }), - }) - .unwrap(); system.run(); }); @@ -2069,8 +1844,8 @@ mod tests { assert_eq!( neighborhood_record, &RouteQueryMessage::data_indefinite_route_request( - Host::new("realdomain.nu", TLS_PORT), - 68 + Some("realdomain.nu".to_string()), + 12 ) ); } @@ -2093,18 +1868,15 @@ mod tests { subject .keys_and_addrs .insert(stream_key.clone(), socket_addr.clone()); - subject.stream_info.insert( - stream_key.clone(), - StreamInfoBuilder::new() - .route(RouteQueryResponse { - route: make_meaningless_route(&CRYPTDE_PAIR), - expected_services: ExpectedServices::RoundTrip( - vec![], - vec![ExpectedService::Nothing], - ), - host: Host::new("booga.com", TLS_PORT), - }) - .build(), + + subject.route_ids_to_return_routes.insert( + 1234, + AddReturnRouteMessage { + return_route_id: 1234, + expected_services: vec![ExpectedService::Nothing], + protocol: ProxyProtocol::TLS, + hostname_opt: None, + }, ); let subject_addr: Addr = subject.start(); let http_request = b"CONNECT https://realdomain.nu:443 HTTP/1.1\r\nHost: https://bunkjunk.wrong:443\r\n\r\n"; @@ -2112,10 +1884,10 @@ mod tests { let inbound_client_data = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr, - reception_port_opt: Some(TLS_PORT), + reception_port: Some(443), last_data: false, is_clandestine: false, - sequence_number_opt: Some(0), + sequence_number: Some(0), data: request_data, }; @@ -2132,7 +1904,7 @@ mod tests { ExpiredCoresPackage::new( SocketAddr::from_str("1.2.3.4:1234").unwrap(), Some(make_wallet("irrelevant")), - return_route(cryptde), + return_route_with_id(cryptde, 1234), client_response_payload.into(), 0, ); @@ -2149,10 +1921,9 @@ mod tests { system.run(); let dispatcher_recording = dispatcher_log_arc.lock().unwrap(); - let record = dispatcher_recording.get_record::(0); - assert_eq!(record.sequence_number_opt.unwrap(), 0); let record = dispatcher_recording.get_record::(1); - assert_eq!(record.sequence_number_opt.unwrap(), 1); + + assert_eq!(record.sequence_number.unwrap(), 1); } #[test] @@ -2165,7 +1936,7 @@ mod tests { let (dispatcher_mock, _dispatcher_awaiter, dispatcher_recording_arc) = make_recorder(); let neighborhood_mock = neighborhood_mock.route_query_response(Some( - zero_hop_route_response(&cryptde.public_key(), cryptde, false), + zero_hop_route_response(&cryptde.public_key(), cryptde), )); let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); @@ -2174,8 +1945,8 @@ mod tests { let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr.clone(), - reception_port_opt: Some(8443), - sequence_number_opt: Some(0), + reception_port: Some(8443), + sequence_number: Some(0), last_data: false, is_clandestine: false, data: request_data.clone(), @@ -2216,7 +1987,7 @@ mod tests { let expected_transmit_data_msg = TransmitDataMsg { endpoint: Endpoint::Socket(socket_addr), last_data: true, - sequence_number_opt: Some(0), + sequence_number: Some(0), data: b"HTTP/1.1 400 Bad Request\r\nContent-Length: 0\r\n\r\n".to_vec(), }; @@ -2236,7 +2007,7 @@ mod tests { let (dispatcher_mock, _dispatcher_awaiter, dispatcher_recording_arc) = make_recorder(); let neighborhood_mock = neighborhood_mock.route_query_response(Some( - zero_hop_route_response(&cryptde.public_key(), cryptde, false), + zero_hop_route_response(&cryptde.public_key(), cryptde), )); let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); @@ -2245,8 +2016,8 @@ mod tests { let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr.clone(), - reception_port_opt: Some(8443), - sequence_number_opt: Some(0), + reception_port: Some(8443), + sequence_number: Some(0), last_data: false, is_clandestine: false, data: request_data.clone(), @@ -2287,7 +2058,7 @@ mod tests { let expected_transmit_data_msg = TransmitDataMsg { endpoint: Endpoint::Socket(socket_addr), last_data: true, - sequence_number_opt: Some(0), + sequence_number: Some(0), data: b"HTTP/1.1 400 Bad Request\r\nContent-Length: 0\r\n\r\n".to_vec(), }; @@ -2311,8 +2082,8 @@ mod tests { let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr.clone(), - reception_port_opt: Some(HTTP_PORT), - sequence_number_opt: Some(0), + reception_port: Some(HTTP_PORT), + sequence_number: Some(0), last_data: true, is_clandestine: false, data: expected_data.clone(), @@ -2346,7 +2117,7 @@ mod tests { &TransmitDataMsg { endpoint: Endpoint::Socket(socket_addr), last_data: true, - sequence_number_opt: Some(0), + sequence_number: Some(0), data: server_impersonator.consuming_wallet_absent(), } ); @@ -2369,8 +2140,8 @@ mod tests { let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr.clone(), - reception_port_opt: Some(TLS_PORT), - sequence_number_opt: Some(0), + reception_port: Some(TLS_PORT), + sequence_number: Some(0), last_data: true, is_clandestine: false, data: expected_data.clone(), @@ -2404,7 +2175,7 @@ mod tests { &TransmitDataMsg { endpoint: Endpoint::Socket(socket_addr), last_data: true, - sequence_number_opt: Some(0), + sequence_number: Some(0), data: server_impersonator.consuming_wallet_absent(), } ); @@ -2421,8 +2192,7 @@ mod tests { let alias_cryptde = CRYPTDE_PAIR.alias.as_ref(); let expected_data = b"GET /index.html HTTP/1.1\r\nHost: nowhere.com\r\n\r\n".to_vec(); let expected_data_inner = expected_data.clone(); - let expected_route = - zero_hop_route_response(main_cryptde.public_key(), main_cryptde, false); + let expected_route = zero_hop_route_response(main_cryptde.public_key(), main_cryptde); let stream_key = StreamKey::make_meaningless_stream_key(); let (hopper, hopper_awaiter, hopper_log_arc) = make_recorder(); let neighborhood = Recorder::new().route_query_response(Some(expected_route.clone())); @@ -2433,8 +2203,8 @@ mod tests { let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr.clone(), - reception_port_opt: Some(HTTP_PORT), - sequence_number_opt: Some(0), + reception_port: Some(HTTP_PORT), + sequence_number: Some(0), last_data: true, is_clandestine: false, data: expected_data_inner, @@ -2443,12 +2213,7 @@ mod tests { let system = System::new("proxy_server_receives_http_request_with_no_consuming_wallet_in_zero_hop_mode_and_handles_normally"); let mut subject = ProxyServer::new(CRYPTDE_PAIR.clone(), false, None, false, false); subject.stream_key_factory = Box::new(stream_key_factory); - subject - .keys_and_addrs - .insert(stream_key.clone(), socket_addr); - subject - .stream_info - .insert(stream_key, StreamInfoBuilder::new().build()); + subject.keys_and_addrs.insert(stream_key, socket_addr); let subject_addr: Addr = subject.start(); let peer_actors = peer_actors_builder() .dispatcher(dispatcher) @@ -2470,7 +2235,7 @@ mod tests { target_component: Component::ProxyClient, return_component_opt: Some(Component::ProxyServer), payload_size: 47, - host: Host::new("nowhere.com", HTTP_PORT), + hostname_opt: Some("nowhere.com".to_string()), } ); let dispatcher_recording = dispatcher_log_arc.lock().unwrap(); @@ -2486,8 +2251,8 @@ mod tests { &ClientRequestPayload_0v1 { stream_key, sequenced_packet: SequencedPacket::new(expected_data, 0, true), - target_hostname: "nowhere.com".to_string(), - target_port: HTTP_PORT, + target_hostname: Some("nowhere.com".to_string()), + target_port: 80, protocol: ProxyProtocol::HTTP, originator_public_key: alias_cryptde.public_key().clone(), } @@ -2506,35 +2271,28 @@ mod tests { let alias_cryptde = CRYPTDE_PAIR.alias.as_ref(); let expected_data = b"Fake TLS request".to_vec(); let expected_data_inner = expected_data.clone(); - let expected_route = zero_hop_route_response(main_cryptde.public_key(), main_cryptde, true); - let expected_route_inner = expected_route.clone(); + let expected_route = zero_hop_route_response(main_cryptde.public_key(), main_cryptde); let stream_key = StreamKey::make_meaningless_stream_key(); let (hopper, hopper_awaiter, hopper_log_arc) = make_recorder(); let neighborhood = Recorder::new().route_query_response(Some(expected_route.clone())); + let neighborhood_log_arc = neighborhood.get_recording(); let (dispatcher, _, dispatcher_log_arc) = make_recorder(); thread::spawn(move || { let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr.clone(), - reception_port_opt: Some(TLS_PORT), - sequence_number_opt: Some(0), + reception_port: Some(TLS_PORT), + sequence_number: Some(0), last_data: true, is_clandestine: false, data: expected_data_inner, }; - let stream_key_factory = StreamKeyFactoryMock::new().make_result(stream_key.clone()); + let stream_key_factory = StreamKeyFactoryMock::new(); // can't make any stream keys; shouldn't have to let system = System::new("proxy_server_receives_tls_request_with_no_consuming_wallet_in_zero_hop_mode_and_handles_normally"); let mut subject = ProxyServer::new(CRYPTDE_PAIR.clone(), false, None, false, false); subject.stream_key_factory = Box::new(stream_key_factory); subject.keys_and_addrs.insert(stream_key, socket_addr); - subject.stream_info.insert( - stream_key, - StreamInfoBuilder::new() - .route(expected_route_inner) - .protocol(ProxyProtocol::TLS) - .build(), - ); let subject_addr: Addr = subject.start(); let peer_actors = peer_actors_builder() .dispatcher(dispatcher) @@ -2548,6 +2306,17 @@ mod tests { system.run(); }); hopper_awaiter.await_message_count(1); + let neighborhood_recording = neighborhood_log_arc.lock().unwrap(); + assert_eq!( + neighborhood_recording.get_record::(0), + &RouteQueryMessage { + target_key_opt: None, + target_component: Component::ProxyClient, + return_component_opt: Some(Component::ProxyServer), + payload_size: 16, + hostname_opt: None, + } + ); let dispatcher_recording = dispatcher_log_arc.lock().unwrap(); assert!(dispatcher_recording.is_empty()); let hopper_recording = hopper_log_arc.lock().unwrap(); @@ -2561,8 +2330,8 @@ mod tests { &ClientRequestPayload_0v1 { stream_key, sequenced_packet: SequencedPacket::new(expected_data, 0, true), - target_hostname: "booga.com".to_string(), - target_port: TLS_PORT, + target_hostname: None, + target_port: 443, protocol: ProxyProtocol::TLS, originator_public_key: alias_cryptde.public_key().clone(), } @@ -2588,8 +2357,8 @@ mod tests { expected_services: ExpectedServices::RoundTrip( vec![make_exit_service_from_key(destination_key.clone())], vec![], + 1234, ), - host: Host::new("booga.com", HTTP_PORT), })); let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); let stream_key = StreamKey::make_meaningless_stream_key(); @@ -2597,8 +2366,8 @@ mod tests { let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr.clone(), - reception_port_opt: Some(HTTP_PORT), - sequence_number_opt: Some(0), + reception_port: Some(HTTP_PORT), + sequence_number: Some(0), last_data: true, is_clandestine: false, data: expected_data.clone(), @@ -2612,7 +2381,7 @@ mod tests { sequence_number: 0, last_data: true, }, - target_hostname: String::from("nowhere.com"), + target_hostname: Some(String::from("nowhere.com")), target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: alias_cryptde.public_key().clone(), @@ -2636,9 +2405,6 @@ mod tests { ); subject.stream_key_factory = Box::new(stream_key_factory); subject.keys_and_addrs.insert(stream_key, socket_addr); - subject - .stream_info - .insert(stream_key.clone(), StreamInfoBuilder::new().build()); let subject_addr: Addr = subject.start(); let peer_actors = peer_actors_builder() .hopper(hopper_mock) @@ -2690,6 +2456,7 @@ mod tests { ), main_cryptde, Some(consuming_wallet), + 1234, Some(TEST_DEFAULT_CHAIN.rec().contract), ) .unwrap(); @@ -2709,8 +2476,8 @@ mod tests { ExpectedService::Nothing, ExpectedService::Exit(PublicKey::new(&[3]), earning_wallet, rate_pack(102)), ], + 1234, ), - host: Host::new("booga.com", HTTP_PORT), })); let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); let stream_key = StreamKey::make_meaningless_stream_key(); @@ -2718,8 +2485,8 @@ mod tests { let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr.clone(), - reception_port_opt: Some(HTTP_PORT), - sequence_number_opt: Some(0), + reception_port: Some(HTTP_PORT), + sequence_number: Some(0), last_data: true, is_clandestine: false, data: expected_data.clone(), @@ -2732,7 +2499,7 @@ mod tests { sequence_number: 0, last_data: true, }, - target_hostname: String::from("nowhere.com"), + target_hostname: Some(String::from("nowhere.com")), target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: alias_cryptde.public_key().clone(), @@ -2775,10 +2542,7 @@ mod tests { let record = recording.get_record::(0); assert_eq!( record, - &RouteQueryMessage::data_indefinite_route_request( - Host::new("nowhere.com", HTTP_PORT), - 47 - ) + &RouteQueryMessage::data_indefinite_route_request(Some("nowhere.com".to_string()), 47) ); } @@ -2796,8 +2560,8 @@ mod tests { expected_services: ExpectedServices::RoundTrip( vec![expected_service.clone()], vec![expected_service], + 123, ), - host: Host::new("booga.com", HTTP_PORT), }); let (neighborhood_mock, _, _) = make_recorder(); let neighborhood_mock = @@ -2808,8 +2572,8 @@ mod tests { let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr.clone(), - reception_port_opt: Some(HTTP_PORT), - sequence_number_opt: Some(0), + reception_port: Some(HTTP_PORT), + sequence_number: Some(0), last_data: true, is_clandestine: false, data: expected_data.clone(), @@ -2868,8 +2632,8 @@ mod tests { let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr.clone(), - reception_port_opt: Some(HTTP_PORT), - sequence_number_opt: Some(0), + reception_port: Some(HTTP_PORT), + sequence_number: Some(0), last_data: true, is_clandestine: false, data: expected_data.clone(), @@ -2920,8 +2684,8 @@ mod tests { let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr.clone(), - reception_port_opt: Some(HTTP_PORT), - sequence_number_opt: Some(0), + reception_port: Some(HTTP_PORT), + sequence_number: Some(0), last_data: true, is_clandestine: false, data: expected_data.clone(), @@ -2955,14 +2719,13 @@ mod tests { let alias_cryptde = CRYPTDE_PAIR.alias.as_ref(); let http_request = b"GET /index.html HTTP/1.1\r\nHost: nowhere.com\r\n\r\n"; let destination_key = PublicKey::from(&b"our destination"[..]); - let route = Route { hops: vec![] }; let route_query_response = RouteQueryResponse { - route: route.clone(), + route: Route { hops: vec![] }, expected_services: ExpectedServices::RoundTrip( vec![make_exit_service_from_key(destination_key.clone())], vec![], + 1234, ), - host: Host::new("booga.com", HTTP_PORT), }; let (hopper_mock, hopper_awaiter, hopper_recording_arc) = make_recorder(); let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); @@ -2970,9 +2733,9 @@ mod tests { let expected_data = http_request.to_vec(); let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), - client_addr: socket_addr, - reception_port_opt: Some(HTTP_PORT), - sequence_number_opt: Some(0), + client_addr: socket_addr.clone(), + reception_port: Some(HTTP_PORT), + sequence_number: Some(0), last_data: true, is_clandestine: false, data: expected_data.clone(), @@ -2984,14 +2747,14 @@ mod tests { sequence_number: 0, last_data: true, }, - target_hostname: String::from("nowhere.com"), + target_hostname: Some(String::from("nowhere.com")), target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: alias_cryptde.public_key().clone(), }; let expected_pkg = IncipientCoresPackage::new( main_cryptde, - route, + Route { hops: vec![] }, expected_payload.into(), &destination_key, ) @@ -3009,12 +2772,8 @@ mod tests { ); subject.stream_key_factory = Box::new(stream_key_factory); subject - .keys_and_addrs - .insert(stream_key.clone(), socket_addr); - subject.stream_info.insert( - stream_key, - StreamInfoBuilder::new().route(route_query_response).build(), - ); + .stream_key_routes + .insert(stream_key, route_query_response); let subject_addr: Addr = subject.start(); let peer_actors = peer_actors_builder().hopper(hopper_mock).build(); subject_addr.try_send(BindMessage { peer_actors }).unwrap(); @@ -3034,89 +2793,57 @@ mod tests { fn proxy_server_sends_message_to_accountant_about_all_services_consumed_on_the_route_over() { let cryptde = CRYPTDE_PAIR.main.as_ref(); let now = SystemTime::now(); - let routing_node_1_public_key = PublicKey::new(&[1]); - let routing_node_2_public_key = PublicKey::new(&[2]); - let exit_node_public_key = PublicKey::new(&[3]); - let key_bytes = b"__originating consuming wallet__"; - let keypair = Bip32EncryptionKeyProvider::from_raw_secret(key_bytes).unwrap(); - let originating_consuming_wallet = Wallet::from(keypair); - let routing_node_1_earning_wallet = make_wallet("route 1 earning wallet"); - let routing_node_2_earning_wallet = make_wallet("route 2 earning wallet"); - let exit_node_earning_wallet = make_wallet("exit earning wallet"); - 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 exit_earning_wallet = make_wallet("exit earning wallet"); + let route_1_earning_wallet = make_wallet("route 1 earning wallet"); + let route_2_earning_wallet = make_wallet("route 2 earning wallet"); let http_request = b"GET /index.html HTTP/1.1\r\nHost: nowhere.com\r\n\r\n"; let (accountant_mock, _, accountant_recording_arc) = make_recorder(); let (hopper_mock, _, hopper_recording_arc) = make_recorder(); let (proxy_server_mock, _, proxy_server_recording_arc) = make_recorder(); - let over_route_segment = RouteSegment::new( - vec![ - &cryptde.public_key(), - &routing_node_1_public_key, - &routing_node_2_public_key, - &exit_node_public_key, - ], - Component::ProxyClient, - ); - let back_route_segment = RouteSegment::new( - vec![ - &exit_node_public_key, - &routing_node_2_public_key, - &routing_node_1_public_key, - &cryptde.public_key(), - ], - Component::ProxyServer, - ); - let route = Route::round_trip( - over_route_segment, - back_route_segment, - cryptde, - Some(originating_consuming_wallet), - Some(TEST_DEFAULT_CHAIN.rec().contract), - ) - .unwrap(); + 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, + route: make_meaningless_route(&CRYPTDE_PAIR), expected_services: ExpectedServices::RoundTrip( vec![ ExpectedService::Nothing, ExpectedService::Routing( - routing_node_1_public_key.clone(), - routing_node_1_earning_wallet.clone(), - routing_node_1_rate_pack.clone(), + PublicKey::new(&[1]), + route_1_earning_wallet.clone(), + routing_node_1_rate_pack, ), ExpectedService::Routing( - routing_node_2_public_key.clone(), - routing_node_2_earning_wallet.clone(), - routing_node_2_rate_pack.clone(), + PublicKey::new(&[2]), + route_2_earning_wallet.clone(), + routing_node_2_rate_pack, ), ExpectedService::Exit( - exit_node_public_key.clone(), - exit_node_earning_wallet.clone(), - exit_node_rate_pack.clone(), + PublicKey::new(&[3]), + exit_earning_wallet.clone(), + exit_node_rate_pack, ), ], vec![ ExpectedService::Exit( - exit_node_public_key.clone(), - exit_node_earning_wallet.clone(), - exit_node_rate_pack, + PublicKey::new(&[3]), + make_wallet("some wallet 1"), + rate_pack(104), ), ExpectedService::Routing( - routing_node_2_public_key.clone(), - routing_node_2_earning_wallet.clone(), - routing_node_2_rate_pack, + PublicKey::new(&[2]), + make_wallet("some wallet 2"), + rate_pack(105), ), ExpectedService::Routing( - routing_node_1_public_key.clone(), - routing_node_1_earning_wallet.clone(), - routing_node_1_rate_pack, + PublicKey::new(&[1]), + make_wallet("some wallet 3"), + rate_pack(106), ), ExpectedService::Nothing, ], + 0, ), - host: Host::new("booga.com", HTTP_PORT), }; let source_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); let stream_key = StreamKey::make_meaningless_stream_key(); @@ -3132,7 +2859,7 @@ mod tests { let payload = ClientRequestPayload_0v1 { stream_key, sequenced_packet: SequencedPacket::new(expected_data, 0, false), - target_hostname: "nowhere.com".to_string(), + target_hostname: Some("nowhere.com".to_string()), target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: PublicKey::new(b"originator_public_key"), @@ -3151,35 +2878,17 @@ mod tests { retire_stream_key_sub_opt: None, }; - let result = ProxyServer::try_transmit_to_hopper(args, route_query_response); + let result = ProxyServer::try_transmit_to_hopper( + args, + peer_actors.proxy_server.add_return_route, + route_query_response, + ); System::current().stop(); system.run(); let recording = hopper_recording_arc.lock().unwrap(); - let mut record = recording.get_record::(0).clone(); + let record = recording.get_record::(0); let payload_enc_length = record.payload.len(); - let _ = record.route.shift(cryptde); - let _ = record.route.shift(&CryptDENull::from( - &routing_node_1_public_key, - TEST_DEFAULT_CHAIN, - )); - let _ = record.route.shift(&CryptDENull::from( - &routing_node_2_public_key, - TEST_DEFAULT_CHAIN, - )); - let _ = record.route.shift(&CryptDENull::from( - &exit_node_public_key, - TEST_DEFAULT_CHAIN, - )); - let _ = record.route.shift(&CryptDENull::from( - &routing_node_2_public_key, - TEST_DEFAULT_CHAIN, - )); - let _ = record.route.shift(&CryptDENull::from( - &routing_node_1_public_key, - TEST_DEFAULT_CHAIN, - )); - let _ = record.route.shift(cryptde); let recording = accountant_recording_arc.lock().unwrap(); let record = recording.get_record::(0); assert_eq!(recording.len(), 1); @@ -3188,7 +2897,7 @@ mod tests { &ReportServicesConsumedMessage { timestamp: now, exit: ExitServiceConsumed { - earning_wallet: exit_node_earning_wallet, + 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 @@ -3196,12 +2905,12 @@ mod tests { routing_payload_size: payload_enc_length, routing: vec![ RoutingServiceConsumed { - earning_wallet: routing_node_1_earning_wallet, + 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: routing_node_2_earning_wallet, + 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, } @@ -3209,7 +2918,8 @@ mod tests { } ); let recording = proxy_server_recording_arc.lock().unwrap(); - assert_eq!(recording.len(), 0); // No StreamShutdownMsg: that's the important thing + let _ = recording.get_record::(0); // don't care about this, other than type + assert_eq!(recording.len(), 1); // No StreamShutdownMsg: that's the important thing assert_eq!(result, Ok(())); } @@ -3223,8 +2933,8 @@ mod tests { expected_services: ExpectedServices::RoundTrip( vec![ExpectedService::Nothing], vec![ExpectedService::Nothing], + 0, ), - host: Host::new("booga.com", HTTP_PORT), }; let source_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); let stream_key = StreamKey::make_meaningless_stream_key(); @@ -3237,7 +2947,7 @@ mod tests { let payload = ClientRequestPayload_0v1 { stream_key, sequenced_packet: SequencedPacket::new(expected_data, 0, false), - target_hostname: "nowhere.com".to_string(), + target_hostname: Some("nowhere.com".to_string()), target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: PublicKey::new(b"originator_public_key"), @@ -3256,12 +2966,26 @@ mod tests { retire_stream_key_sub_opt: Some(peer_actors.proxy_server.stream_shutdown_sub), }; - let result = ProxyServer::try_transmit_to_hopper(args, route_query_response); + let result = ProxyServer::try_transmit_to_hopper( + args, + peer_actors.proxy_server.add_return_route, + route_query_response, + ); System::current().stop(); system.run(); let recording = proxy_server_recording_arc.lock().unwrap(); - let record = recording.get_record::(0); + let record = recording.get_record::(0); + assert_eq!( + record, + &AddReturnRouteMessage { + return_route_id: 0, + expected_services: vec![ExpectedService::Nothing], + protocol: ProxyProtocol::HTTP, + hostname_opt: Some("nowhere.com".to_string()) + } + ); + let record = recording.get_record::(1); assert_eq!( record, &StreamShutdownMsg { @@ -3283,8 +3007,7 @@ mod tests { let http_request = b"GET /index.html HTTP/1.1\r\nHost: nowhere.com\r\n\r\n"; let (accountant_mock, accountant_awaiter, _) = make_recorder(); let (neighborhood_mock, _, _) = make_recorder(); - let mut route_query_response = - zero_hop_route_response(&cryptde.public_key(), cryptde, false); + 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(), @@ -3292,6 +3015,7 @@ mod tests { rate_pack(3), )], vec![], + 0, ); let neighborhood_mock = neighborhood_mock.route_query_response(Some(route_query_response.clone())); @@ -3301,8 +3025,8 @@ mod tests { let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr.clone(), - reception_port_opt: Some(HTTP_PORT), - sequence_number_opt: Some(0), + reception_port: Some(HTTP_PORT), + sequence_number: Some(0), last_data: true, is_clandestine: false, data: expected_data.clone(), @@ -3337,35 +3061,28 @@ mod tests { #[test] #[should_panic( - expected = "AddRouteResultMessage Handler: dns_failure_retry_opt is None for stream key" + expected = "AddRouteResultMessage Handler: stream key: AAAAAAAAAAAAAAAAAAAAAAAAAAA not found within dns_failure_retries" )] - fn route_result_message_handler_panics_when_no_dns_retries_exist() { - init_test_logging(); - let system = System::new("route_result_message_handler_panics_when_no_dns_retries_exist"); - let mut subject = ProxyServer::new( + fn route_result_message_handler_panics_when_dns_retries_hashmap_doesnt_contain_a_stream_key() { + let system = System::new("route_result_message_handler_panics_when_dns_retries_hashmap_doesnt_contain_a_stream_key"); + let subject = ProxyServer::new( CRYPTDE_PAIR.clone(), true, Some(STANDARD_CONSUMING_WALLET_BALANCE), false, false, ); - let stream_key = StreamKey::make_meaningless_stream_key(); - subject.stream_info.insert( - stream_key.clone(), - StreamInfoBuilder::new().build(), // no DNS retries - ); let subject_addr: Addr = subject.start(); let peer_actors = peer_actors_builder().build(); subject_addr.try_send(BindMessage { peer_actors }).unwrap(); subject_addr .try_send(AddRouteResultMessage { - stream_key, + stream_key: StreamKey::make_meaningless_stream_key(), result: Err("Some Error".to_string()), }) .unwrap(); - System::current().stop(); system.run(); } @@ -3427,8 +3144,8 @@ mod tests { let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr.clone(), - reception_port_opt: Some(HTTP_PORT), - sequence_number_opt: Some(0), + reception_port: Some(HTTP_PORT), + sequence_number: Some(0), last_data: true, data: expected_data.clone(), is_clandestine: false, @@ -3464,7 +3181,7 @@ mod tests { let expected_msg = TransmitDataMsg { endpoint: Endpoint::Socket(SocketAddr::from_str("1.2.3.4:5678").unwrap()), last_data: true, - sequence_number_opt: Some(0), + sequence_number: Some(0), data: ServerImpersonatorHttp {}.route_query_failure_response("nowhere.com"), }; assert_eq!(record, &expected_msg); @@ -3472,13 +3189,10 @@ mod tests { let record = recording.get_record::(0); assert_eq!( record, - &RouteQueryMessage::data_indefinite_route_request( - Host::new("nowhere.com", HTTP_PORT), - 47 - ) + &RouteQueryMessage::data_indefinite_route_request(Some("nowhere.com".to_string()), 47) ); TestLogHandler::new().exists_log_containing(&format!( - "WARN: {test_name}: No route found for hostname: \"nowhere.com\" - stream key {stream_key} - retries left: 3 - AddRouteResultMessage Error: Failed to find route to nowhere.com" + "WARN: {test_name}: No route found for hostname: Some(\"nowhere.com\") - stream key {stream_key} - retries left: 3 - AddRouteResultMessage Error: Failed to find route to nowhere.com" )); } @@ -3509,7 +3223,6 @@ mod tests { rate_pack(103), ), ]), - host: Host::new("booga.com", HTTP_PORT), }; let payload = ClientRequestPayload_0v1 { stream_key: StreamKey::make_meaningless_stream_key(), @@ -3518,7 +3231,7 @@ mod tests { sequence_number: 0, last_data: false, }, - target_hostname: "booga.com".to_string(), + target_hostname: None, target_port: 0, protocol: ProxyProtocol::TLS, originator_public_key: cryptde.public_key().clone(), @@ -3538,7 +3251,11 @@ mod tests { retire_stream_key_sub_opt: None, }; - let _result = ProxyServer::try_transmit_to_hopper(args, route_result); + let _result = ProxyServer::try_transmit_to_hopper( + args, + peer_actors.proxy_server.add_return_route, + route_result, + ); } #[test] @@ -3552,20 +3269,25 @@ mod tests { false, false, ); - let 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), - ), - ]; + 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, + hostname_opt: None, + }; - subject.report_response_services_consumed(&expected_services, 1234, 3456); + subject.report_response_services_consumed(&add_return_route_message, 1234, 3456); } #[test] @@ -3583,11 +3305,11 @@ mod tests { RouteSegment::new(vec![public_key, public_key], Component::ProxyServer), cryptde, None, + 1234, None, ) .unwrap(), - expected_services: ExpectedServices::RoundTrip(vec![], vec![]), - host: Host::new("booga.com", HTTP_PORT), + expected_services: ExpectedServices::RoundTrip(vec![], vec![], 1234), }; let neighborhood_mock = neighborhood_mock.route_query_response(Some(route_query_response)); let dispatcher = Recorder::new(); @@ -3598,8 +3320,8 @@ mod tests { let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr.clone(), - reception_port_opt: Some(HTTP_PORT), - sequence_number_opt: Some(0), + reception_port: Some(HTTP_PORT), + sequence_number: Some(0), last_data: true, data: expected_data.clone(), is_clandestine: false, @@ -3636,7 +3358,7 @@ mod tests { let expected_msg = TransmitDataMsg { endpoint: Endpoint::Socket(SocketAddr::from_str("1.2.3.4:5678").unwrap()), last_data: true, - sequence_number_opt: Some(0), + sequence_number: Some(0), data: ServerImpersonatorHttp {}.route_query_failure_response("nowhere.com"), }; assert_eq!(record, &expected_msg); @@ -3644,19 +3366,35 @@ mod tests { let record = recording.get_record::(0); assert_eq!( record, - &RouteQueryMessage::data_indefinite_route_request( - Host::new("nowhere.com", HTTP_PORT), - 47 - ) + &RouteQueryMessage::data_indefinite_route_request(Some("nowhere.com".to_string()), 47) ); TestLogHandler::new().exists_log_containing(&format!( - "WARN: {test_name}: No route found for hostname: \"nowhere.com\" - stream key {stream_key} - retries left: 3 - AddRouteResultMessage Error: Failed to find route to nowhere.com" + "WARN: {test_name}: No route found for hostname: Some(\"nowhere.com\") - stream key {stream_key} - retries left: 3 - AddRouteResultMessage Error: Failed to find route to nowhere.com" )); } #[test] fn proxy_server_receives_tls_client_hello_from_dispatcher_then_sends_cores_package_to_hopper() { - let tls_request = make_server_com_client_hello(); + let tls_request = &[ + 0x16, // content_type: Handshake + 0x00, 0x00, 0x00, 0x00, // version, length: don't care + 0x01, // handshake_type: ClientHello + 0x00, 0x00, 0x00, 0x00, 0x00, // length, version: don't care + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, // random: don't care + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, // random: don't care + 0x00, // session_id_length + 0x00, 0x00, // cipher_suites_length + 0x00, // compression_methods_length + 0x00, 0x13, // extensions_length + 0x00, 0x00, // extension_type: server_name + 0x00, 0x0F, // extension_length + 0x00, 0x0D, // server_name_list_length + 0x00, // server_name_type + 0x00, 0x0A, // server_name_length + b's', b'e', b'r', b'v', b'e', b'r', b'.', b'c', b'o', b'm', // server_name + ]; let main_cryptde = CRYPTDE_PAIR.main.as_ref(); let alias_cryptde = CRYPTDE_PAIR.alias.as_ref(); let hopper_mock = Recorder::new(); @@ -3668,22 +3406,22 @@ mod tests { expected_services: ExpectedServices::RoundTrip( vec![make_exit_service_from_key(destination_key.clone())], vec![], + 1234, ), - host: Host::new("booga.com", TLS_PORT), })); let stream_key = StreamKey::make_meaningless_stream_key(); let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); - let expected_data = tls_request.clone(); + let expected_data = tls_request.to_vec(); let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr.clone(), - reception_port_opt: Some(TLS_PORT), - sequence_number_opt: Some(0), + reception_port: Some(TLS_PORT), + sequence_number: Some(0), last_data: false, is_clandestine: false, data: expected_data.clone(), }; - let expected_tls_request = PlainData::new(tls_request.as_slice()); + let expected_tls_request = PlainData::new(tls_request); let route = Route { hops: vec![] }; let expected_payload = ClientRequestPayload_0v1 { stream_key: stream_key.clone(), @@ -3692,7 +3430,7 @@ mod tests { sequence_number: 0, last_data: false, }, - target_hostname: String::from("server.com"), + target_hostname: Some(String::from("server.com")), target_port: TLS_PORT, protocol: ProxyProtocol::TLS, originator_public_key: alias_cryptde.public_key().clone(), @@ -3753,8 +3491,8 @@ mod tests { expected_services: ExpectedServices::RoundTrip( vec![make_exit_service_from_key(destination_key.clone())], vec![], + 1234, ), - host: Host::new("booga.com", TLS_PORT), })); let stream_key = StreamKey::make_meaningless_stream_key(); let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); @@ -3762,8 +3500,8 @@ mod tests { let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr.clone(), - reception_port_opt: Some(TLS_PORT), - sequence_number_opt: Some(0), + reception_port: Some(TLS_PORT), + sequence_number: Some(0), last_data: false, is_clandestine: false, data: expected_data.clone(), @@ -3777,7 +3515,7 @@ mod tests { sequence_number: 0, last_data: false, }, - target_hostname: "booga.com".to_string(), + target_hostname: None, target_port: TLS_PORT, protocol: ProxyProtocol::TLS, originator_public_key: alias_cryptde.public_key().clone(), @@ -3799,22 +3537,6 @@ mod tests { ); subject.stream_key_factory = Box::new(StreamKeyFactoryMock::new().make_result(stream_key.clone())); - subject - .keys_and_addrs - .insert(stream_key.clone(), socket_addr); - subject.stream_info.insert( - stream_key.clone(), - StreamInfoBuilder::new() - .route(RouteQueryResponse { - route: Route { hops: vec![] }, - expected_services: ExpectedServices::RoundTrip( - vec![make_exit_service_from_key(destination_key.clone())], - vec![], - ), - host: Host::new("booga.com", TLS_PORT), - }) - .build(), - ); 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 peer_actors = peer_actors_builder() @@ -3827,6 +3549,7 @@ mod tests { system.run(); }); + hopper_awaiter.await_message_count(1); let recording = hopper_log_arc.lock().unwrap(); let record = recording.get_record::(0); @@ -3852,8 +3575,8 @@ mod tests { expected_services: ExpectedServices::RoundTrip( vec![make_exit_service_from_key(destination_key.clone())], vec![], + 1234, ), - host: Host::new("booga.com", TLS_PORT), })); let client_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); let stream_key = StreamKey::make_meaningful_stream_key(test_name); @@ -3861,8 +3584,8 @@ mod tests { let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr, - reception_port_opt: Some(TLS_PORT), - sequence_number_opt: Some(0), + reception_port: Some(TLS_PORT), + sequence_number: Some(0), last_data: true, is_clandestine: false, data: expected_data.clone(), @@ -3876,7 +3599,7 @@ mod tests { sequence_number: 0, last_data: true, }, - target_hostname: "booga.com".to_string(), + target_hostname: None, target_port: TLS_PORT, protocol: ProxyProtocol::TLS, originator_public_key: alias_cryptde.public_key().clone(), @@ -3897,20 +3620,6 @@ mod tests { false, ); subject.keys_and_addrs.insert(stream_key, client_addr); - subject.stream_info.insert( - stream_key, - StreamInfoBuilder::new() - .route(RouteQueryResponse { - route: Route { hops: vec![] }, - expected_services: ExpectedServices::RoundTrip( - vec![make_exit_service_from_key(destination_key.clone())], - vec![], - ), - host: Host::new("booga.com", TLS_PORT), - }) - .protocol(ProxyProtocol::TLS) - .build(), - ); let system = System::new(test_name); let subject_addr: Addr = subject.start(); let peer_actors = peer_actors_builder() @@ -3964,8 +3673,8 @@ mod tests { let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr.clone(), - reception_port_opt: Some(TLS_PORT), - sequence_number_opt: Some(0), + reception_port: Some(TLS_PORT), + sequence_number: Some(0), last_data: true, data: tls_request, is_clandestine: false, @@ -3999,7 +3708,7 @@ mod tests { let expected_msg = TransmitDataMsg { endpoint: Endpoint::Socket(SocketAddr::from_str("1.2.3.4:5678").unwrap()), last_data: true, - sequence_number_opt: Some(0), + sequence_number: Some(0), data: ServerImpersonatorTls {}.route_query_failure_response("ignored"), }; assert_eq!(record, &expected_msg); @@ -4025,22 +3734,17 @@ mod tests { subject .keys_and_addrs .insert(stream_key.clone(), socket_addr.clone()); - subject.stream_info.insert( - stream_key.clone(), - StreamInfoBuilder::new() - .route(RouteQueryResponse { - route: Route { hops: vec![] }, - expected_services: ExpectedServices::RoundTrip( - vec![], - vec![ExpectedService::Nothing], - ), - host: Host::new("booga.com", TLS_PORT), - }) - .protocol(ProxyProtocol::TLS) - .build(), + subject.route_ids_to_return_routes.insert( + 1234, + AddReturnRouteMessage { + return_route_id: 1234, + expected_services: vec![ExpectedService::Nothing], + protocol: ProxyProtocol::TLS, + hostname_opt: None, + }, ); let subject_addr: Addr = subject.start(); - let remaining_route = return_route(cryptde); + let remaining_route = return_route_with_id(cryptde, 1234); let client_response_payload = ClientResponsePayload_0v1 { stream_key: stream_key.clone(), sequenced_packet: SequencedPacket { @@ -4076,7 +3780,7 @@ mod tests { stream_key )); tlh.exists_log_containing(&format!( - "ERROR: {test_name}: Can't pay for return services consumed: received response with unrecognized stream key {:?}. Ignoring", + "WARN: {test_name}: Discarding 16-byte packet 12345678 from an unrecognized stream key: {:?}", stream_key )); } @@ -4115,17 +3819,24 @@ mod tests { subject .keys_and_addrs .insert(stream_key.clone(), socket_addr.clone()); - subject.stream_info.insert( + subject.stream_key_routes.insert( stream_key.clone(), - StreamInfoBuilder::new() - .route(RouteQueryResponse { - route: Route { hops: vec![] }, - expected_services: ExpectedServices::RoundTrip(vec![], vec![]), - host: Host::new("booga.com", HTTP_PORT), - }) - .protocol(ProxyProtocol::HTTP) - .tunneled_host("hostname") - .build(), + RouteQueryResponse { + route: Route { hops: vec![] }, + expected_services: ExpectedServices::RoundTrip(vec![], vec![], 1234), + }, + ); + subject + .tunneled_hosts + .insert(stream_key.clone(), "hostname".to_string()); + subject.route_ids_to_return_routes.insert( + 1234, + AddReturnRouteMessage { + return_route_id: 1234, + expected_services: vec![], + protocol: ProxyProtocol::HTTP, + hostname_opt: None, + }, ); let client_response_payload = ClientResponsePayload_0v1 { stream_key: stream_key.clone(), @@ -4138,7 +3849,7 @@ mod tests { ExpiredCoresPackage::new( SocketAddr::from_str("1.2.3.4:1234").unwrap(), Some(make_wallet("irrelevant")), - return_route(cryptde), + return_route_with_id(cryptde, 1234), client_response_payload.into(), 0, ); @@ -4146,7 +3857,8 @@ mod tests { subject.handle_client_response_payload(expired_cores_package); assert!(subject.keys_and_addrs.is_empty()); - assert!(subject.stream_info.get(&stream_key).is_none()); + assert!(subject.stream_key_routes.is_empty()); + assert!(subject.tunneled_hosts.is_empty()); } #[test] @@ -4214,17 +3926,24 @@ mod tests { subject .keys_and_addrs .insert(stream_key.clone(), msg.peer_addr.clone()); - subject.stream_info.insert( + subject.stream_key_routes.insert( stream_key.clone(), - StreamInfoBuilder::new() - .route(RouteQueryResponse { - route: Route { hops: vec![] }, - expected_services: ExpectedServices::RoundTrip(vec![], vec![]), - host: Host::new("booga.com", HTTP_PORT), - }) - .protocol(ProxyProtocol::HTTP) - .tunneled_host("hostname") - .build(), + RouteQueryResponse { + route: Route { hops: vec![] }, + expected_services: ExpectedServices::RoundTrip(vec![], vec![], 1234), + }, + ); + subject + .tunneled_hosts + .insert(stream_key.clone(), "hostname".to_string()); + subject.route_ids_to_return_routes.insert( + 1234, + AddReturnRouteMessage { + return_route_id: 1234, + expected_services: vec![], + protocol: ProxyProtocol::HTTP, + hostname_opt: None, + }, ); let proxy_server_addr = subject.start(); let schedule_stream_key_purge_sub = proxy_server_addr.clone().recipient(); @@ -4242,14 +3961,18 @@ mod tests { .unwrap(); let pre_purge_assertions = AssertionsMessage { assertions: Box::new(move |proxy_server: &mut ProxyServer| { - let stream_info = proxy_server.stream_info.get(&stream_key).unwrap(); - let purge_timestamp = stream_info.time_to_live_opt.unwrap(); + let purge_timestamp = proxy_server + .stream_key_ttl + .get(&stream_key) + .unwrap() + .clone(); assert!( time_before_sending_package <= purge_timestamp && purge_timestamp <= time_after_sending_package ); - assert!(!proxy_server.stream_info.get(&stream_key).is_none()); assert!(!proxy_server.keys_and_addrs.is_empty()); + assert!(!proxy_server.stream_key_routes.is_empty()); + assert!(!proxy_server.tunneled_hosts.is_empty()); TestLogHandler::new().exists_log_containing(&format!( "DEBUG: {test_name}: Client closed stream referenced by stream key {:?}, \ which was tunneling to the host \"hostname\". \ @@ -4267,7 +3990,9 @@ mod tests { let post_purge_assertions = AssertionsMessage { assertions: Box::new(move |proxy_server: &mut ProxyServer| { assert!(proxy_server.keys_and_addrs.is_empty()); - assert!(proxy_server.stream_info.get(&stream_key).is_none()); + assert!(proxy_server.stream_key_routes.is_empty()); + assert!(proxy_server.tunneled_hosts.is_empty()); + assert!(proxy_server.stream_key_ttl.is_empty()); TestLogHandler::new().exists_log_containing(&format!( "DEBUG: {test_name}: Retiring stream key {:?}", stream_key @@ -4303,6 +4028,16 @@ mod tests { subject .keys_and_addrs .insert(stream_key.clone(), socket_addr.clone()); + subject.stream_key_routes.insert( + stream_key.clone(), + RouteQueryResponse { + route: Route { hops: vec![] }, + expected_services: ExpectedServices::RoundTrip(vec![], vec![], 1234), + }, + ); + subject + .tunneled_hosts + .insert(stream_key.clone(), "hostname".to_string()); let exit_key = PublicKey::new(&b"blah"[..]); let exit_wallet = make_wallet("abc"); let exit_rates = RatePack { @@ -4311,26 +4046,22 @@ mod tests { exit_byte_rate: 100, exit_service_rate: 60000, }; - subject.stream_info.insert( - stream_key.clone(), - StreamInfoBuilder::new() - .route(RouteQueryResponse { - route: Route { hops: vec![] }, - expected_services: ExpectedServices::RoundTrip( - vec![], - vec![ExpectedService::Exit( - exit_key, - exit_wallet.clone(), - exit_rates.clone(), - )], - ), - host: Host::new("booga.com", HTTP_PORT), - }) - .tunneled_host("hostname") - .protocol(ProxyProtocol::HTTP) - .time_to_live(SystemTime::now()) - .build(), + subject.route_ids_to_return_routes.insert( + 1234, + AddReturnRouteMessage { + return_route_id: 1234, + expected_services: vec![ExpectedService::Exit( + exit_key, + exit_wallet.clone(), + exit_rates.clone(), + )], + protocol: ProxyProtocol::HTTP, + hostname_opt: None, + }, ); + subject + .stream_key_ttl + .insert(stream_key.clone(), SystemTime::now()); let (accountant, _, accountant_recording_arc) = make_recorder(); let (dispatcher, _, dispatcher_recording_arc) = make_recorder(); let proxy_server_addr = subject.start(); @@ -4348,7 +4079,7 @@ mod tests { ExpiredCoresPackage::new( SocketAddr::from_str("1.2.3.4:1234").unwrap(), Some(make_wallet("irrelevant")), - return_route(cryptde), + return_route_with_id(cryptde, 1234), client_response_payload.into(), 5432, ); @@ -4406,35 +4137,63 @@ mod tests { let rate_pack_d = rate_pack(101); let rate_pack_e = rate_pack(102); let rate_pack_f = rate_pack(103); - subject.stream_info.insert( - stream_key.clone(), - StreamInfoBuilder::new() - .route(RouteQueryResponse { - route: Route { hops: vec![] }, - expected_services: ExpectedServices::RoundTrip( - vec![], - vec![ - ExpectedService::Exit( - irrelevant_public_key.clone(), - incoming_route_d_wallet.clone(), - rate_pack_d, - ), - ExpectedService::Routing( - irrelevant_public_key.clone(), - incoming_route_e_wallet.clone(), - rate_pack_e, - ), - ExpectedService::Routing( - irrelevant_public_key.clone(), - incoming_route_f_wallet.clone(), - rate_pack_f, - ), - ExpectedService::Nothing, - ], + subject.route_ids_to_return_routes.insert( + 1234, + AddReturnRouteMessage { + return_route_id: 1234, + expected_services: vec![ + ExpectedService::Exit( + irrelevant_public_key.clone(), + incoming_route_d_wallet.clone(), + rate_pack_d, ), - host: Host::new("booga.com", HTTP_PORT), - }) - .build(), + ExpectedService::Routing( + irrelevant_public_key.clone(), + incoming_route_e_wallet.clone(), + rate_pack_e, + ), + ExpectedService::Routing( + irrelevant_public_key.clone(), + incoming_route_f_wallet.clone(), + rate_pack_f, + ), + ExpectedService::Nothing, + ], + protocol: ProxyProtocol::TLS, + hostname_opt: None, + }, + ); + 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 { + return_route_id: 1235, + expected_services: vec![ + ExpectedService::Exit( + irrelevant_public_key.clone(), + incoming_route_g_wallet.clone(), + rate_pack_g, + ), + ExpectedService::Routing( + irrelevant_public_key.clone(), + incoming_route_h_wallet.clone(), + rate_pack_h, + ), + ExpectedService::Routing( + irrelevant_public_key.clone(), + incoming_route_i_wallet.clone(), + rate_pack_i, + ), + ExpectedService::Nothing, + ], + protocol: ProxyProtocol::TLS, + hostname_opt: None, + }, ); let subject_addr: Addr = subject.start(); let first_client_response_payload = ClientResponsePayload_0v1 { @@ -4450,7 +4209,7 @@ mod tests { ExpiredCoresPackage::new( SocketAddr::from_str("1.2.3.4:1234").unwrap(), Some(make_wallet("irrelevant")), - return_route(cryptde), + return_route_with_id(cryptde, 1234), first_client_response_payload.into(), 0, ); @@ -4468,7 +4227,7 @@ mod tests { ExpiredCoresPackage::new( SocketAddr::from_str("1.2.3.5:1235").unwrap(), Some(make_wallet("irrelevant")), - return_route(cryptde), + return_route_with_id(cryptde, 1235), second_client_response_payload.into(), 0, ); @@ -4506,7 +4265,7 @@ mod tests { &ReportServicesConsumedMessage { timestamp: first_report_timestamp, exit: ExitServiceConsumed { - earning_wallet: incoming_route_d_wallet.clone(), + 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 @@ -4514,12 +4273,12 @@ mod tests { routing_payload_size: routing_size, routing: vec![ RoutingServiceConsumed { - earning_wallet: incoming_route_e_wallet.clone(), + 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.clone(), + earning_wallet: incoming_route_f_wallet, service_rate: rate_pack_f.routing_service_rate, byte_rate: rate_pack_f.routing_byte_rate } @@ -4535,22 +4294,22 @@ mod tests { &ReportServicesConsumedMessage { timestamp: second_report_timestamp, exit: ExitServiceConsumed { - earning_wallet: incoming_route_d_wallet, + earning_wallet: incoming_route_g_wallet, payload_size: second_exit_size, - service_rate: rate_pack_d.exit_service_rate, - byte_rate: rate_pack_d.exit_byte_rate + 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_e_wallet, - service_rate: rate_pack_e.routing_service_rate, - byte_rate: rate_pack_e.routing_byte_rate + 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_f_wallet, - service_rate: rate_pack_f.routing_service_rate, - byte_rate: rate_pack_f.routing_byte_rate + earning_wallet: incoming_route_i_wallet, + service_rate: rate_pack_i.routing_service_rate, + byte_rate: rate_pack_i.routing_byte_rate } ] } @@ -4580,52 +4339,52 @@ mod tests { .keys_and_addrs .insert(stream_key.clone(), socket_addr.clone()); subject.logger = Logger::new(test_name); + let mut dns_failure_retries_hash_map = HashMap::new(); let mut dns_fail_client_payload = make_request_payload(111, cryptde); dns_fail_client_payload.stream_key = stream_key; + dns_failure_retries_hash_map.insert( + stream_key, + DNSFailureRetry { + unsuccessful_request: dns_fail_client_payload, + retries_left: 3, + }, + ); + subject.dns_failure_retries = dns_failure_retries_hash_map; 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.stream_info.insert( - stream_key_clone.clone(), - StreamInfoBuilder::new() - .dns_failure_retry(DNSFailureRetry { - unsuccessful_request: dns_fail_client_payload, - retries_left: 3, - }) - .route(RouteQueryResponse { - route: Route { hops: vec![] }, - expected_services: ExpectedServices::RoundTrip( - vec![], - vec![ - ExpectedService::Exit( - irrelevant_public_key.clone(), - incoming_route_d_wallet.clone(), - rate_pack_d, - ), - ExpectedService::Routing( - irrelevant_public_key.clone(), - incoming_route_e_wallet.clone(), - rate_pack_e, - ), - ExpectedService::Routing( - irrelevant_public_key.clone(), - incoming_route_f_wallet.clone(), - rate_pack_f, - ), - ExpectedService::Nothing, - ], + subject.route_ids_to_return_routes.insert( + 1234, + AddReturnRouteMessage { + return_route_id: 1234, + expected_services: vec![ + ExpectedService::Exit( + irrelevant_public_key.clone(), + incoming_route_d_wallet.clone(), + rate_pack_d, ), - host: Host::new("booga.com", TLS_PORT), - }) - .protocol(ProxyProtocol::TLS) - .build(), - ); - let subject_addr: Addr = subject.start(); - let first_client_response_payload = ClientResponsePayload_0v1 { - stream_key, + ExpectedService::Routing( + irrelevant_public_key.clone(), + incoming_route_e_wallet.clone(), + rate_pack_e, + ), + ExpectedService::Routing( + irrelevant_public_key.clone(), + incoming_route_f_wallet.clone(), + rate_pack_f, + ), + ExpectedService::Nothing, + ], + protocol: ProxyProtocol::TLS, + hostname_opt: None, + }, + ); + let subject_addr: Addr = subject.start(); + let first_client_response_payload = ClientResponsePayload_0v1 { + stream_key, sequenced_packet: SequencedPacket { data: b"some data".to_vec(), sequence_number: 4321, @@ -4636,7 +4395,7 @@ mod tests { ExpiredCoresPackage::new( SocketAddr::from_str("1.2.3.4:1234").unwrap(), Some(make_wallet("irrelevant")), - return_route(cryptde), + return_route_with_id(cryptde, 1234), first_client_response_payload.into(), 0, ); @@ -4648,16 +4407,14 @@ mod tests { subject_addr .try_send(AssertionsMessage { assertions: Box::new(move |proxy_server: &mut ProxyServer| { - let retry_opt = &proxy_server - .stream_info(&stream_key_clone) - .unwrap() - .dns_failure_retry_opt; - assert!(retry_opt.is_none()); + let retry_opt = proxy_server.dns_failure_retries.get(&stream_key); + assert_eq!(retry_opt, None); }), }) .unwrap(); System::current().stop(); system.run(); + TestLogHandler::new().exists_log_containing(&format!("DEBUG: {test_name}: Successful attempt of DNS resolution, removing DNS retry entry for stream key: {stream_key_clone}")); } #[test] @@ -4681,30 +4438,25 @@ mod tests { let incoming_route_e_wallet = make_wallet("E Earning"); let rate_pack_d = rate_pack(101); let rate_pack_e = rate_pack(102); - subject.stream_info.insert( - stream_key.clone(), - StreamInfoBuilder::new() - .route(RouteQueryResponse { - route: Route { hops: vec![] }, - expected_services: ExpectedServices::RoundTrip( - vec![], - vec![ - ExpectedService::Exit( - irrelevant_public_key.clone(), - incoming_route_d_wallet.clone(), - rate_pack_d, - ), - ExpectedService::Routing( - irrelevant_public_key.clone(), - incoming_route_e_wallet.clone(), - rate_pack_e, - ), - ], + subject.route_ids_to_return_routes.insert( + 1234, + AddReturnRouteMessage { + return_route_id: 1234, + expected_services: vec![ + ExpectedService::Exit( + irrelevant_public_key.clone(), + incoming_route_d_wallet.clone(), + rate_pack_d, ), - host: Host::new("booga.com", TLS_PORT), - }) - .protocol(ProxyProtocol::TLS) - .build(), + ExpectedService::Routing( + irrelevant_public_key.clone(), + incoming_route_e_wallet.clone(), + rate_pack_e, + ), + ], + protocol: ProxyProtocol::TLS, + hostname_opt: None, + }, ); let subject_addr: Addr = subject.start(); let client_response_payload = ClientResponsePayload_0v1 { @@ -4720,7 +4472,7 @@ mod tests { ExpiredCoresPackage::new( SocketAddr::from_str("1.2.3.4:1234").unwrap(), Some(make_wallet("irrelevant")), - return_route(cryptde), + return_route_with_id(cryptde, 1234), client_response_payload.into(), 0, ); @@ -4782,47 +4534,46 @@ mod tests { let stream_key = StreamKey::make_meaningless_stream_key(); let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); + let mut dns_failure_retries_hash_map = HashMap::new(); let client_payload = make_request_payload(111, cryptde); - let exit_public_key = PublicKey::from(&b"exit_key"[..]); - let exit_wallet = make_wallet("exit wallet"); + dns_failure_retries_hash_map.insert( + stream_key, + DNSFailureRetry { + unsuccessful_request: client_payload, + retries_left: 0, + }, + ); + subject.dns_failure_retries = dns_failure_retries_hash_map; subject .keys_and_addrs .insert(stream_key.clone(), socket_addr.clone()); - subject.stream_info.insert( - stream_key.clone(), - StreamInfoBuilder::new() - .dns_failure_retry(DNSFailureRetry { - unsuccessful_request: client_payload, - retries_left: 0, - }) - .route(RouteQueryResponse { - route: Route { hops: vec![] }, - expected_services: ExpectedServices::RoundTrip( - vec![], - vec![ExpectedService::Exit( - exit_public_key.clone(), - exit_wallet, - rate_pack(10), - )], - ), - host: Host::new("server.com", HTTP_PORT), - }) - .protocol(ProxyProtocol::HTTP) - .build(), - ); + 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(), Some(make_wallet("irrelevant")), - return_route(cryptde), + return_route_with_id(cryptde, 1234), dns_resolve_failure.into(), 0, ); let peer_actors = peer_actors_builder().dispatcher(dispatcher_mock).build(); subject_addr.try_send(BindMessage { peer_actors }).unwrap(); + subject_addr + .try_send(AddReturnRouteMessage { + return_route_id: 1234, + expected_services: vec![ExpectedService::Exit( + exit_public_key.clone(), + exit_wallet, + rate_pack(10), + )], + protocol: ProxyProtocol::HTTP, + hostname_opt: Some("server.com".to_string()), + }) + .unwrap(); subject_addr.try_send(expired_cores_package).unwrap(); System::current().stop(); @@ -4834,9 +4585,9 @@ mod tests { TransmitDataMsg { endpoint: Endpoint::Socket(socket_addr), last_data: true, - sequence_number_opt: Some(0), + sequence_number: Some(0), data: ServerImpersonatorHttp {} - .dns_resolution_failure_response("server.com".to_string()), + .dns_resolution_failure_response(Some("server.com".to_string()),), }, *record ); @@ -4857,7 +4608,16 @@ mod tests { let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); let stream_key = StreamKey::make_meaningless_stream_key(); let irrelevant_public_key = PublicKey::from(&b"irrelevant"[..]); + let mut dns_failure_retries_hash_map = HashMap::new(); let client_payload = make_request_payload(111, cryptde); + dns_failure_retries_hash_map.insert( + stream_key, + DNSFailureRetry { + unsuccessful_request: client_payload, + retries_left: 0, + }, + ); + subject.dns_failure_retries = dns_failure_retries_hash_map; subject .keys_and_addrs .insert(stream_key.clone(), socket_addr.clone()); @@ -4867,40 +4627,31 @@ mod tests { let rate_pack_d = rate_pack(101); let rate_pack_e = rate_pack(102); let rate_pack_f = rate_pack(103); - subject.stream_info.insert( - stream_key.clone(), - StreamInfoBuilder::new() - .dns_failure_retry(DNSFailureRetry { - unsuccessful_request: client_payload, - retries_left: 0, - }) - .route(RouteQueryResponse { - route: Route { hops: vec![] }, - expected_services: ExpectedServices::RoundTrip( - vec![], - vec![ - ExpectedService::Exit( - irrelevant_public_key.clone(), - incoming_route_d_wallet.clone(), - rate_pack_d, - ), - ExpectedService::Routing( - irrelevant_public_key.clone(), - incoming_route_e_wallet.clone(), - rate_pack_e, - ), - ExpectedService::Routing( - irrelevant_public_key.clone(), - incoming_route_f_wallet.clone(), - rate_pack_f, - ), - ExpectedService::Nothing, - ], + subject.route_ids_to_return_routes.insert( + 1234, + AddReturnRouteMessage { + return_route_id: 1234, + expected_services: vec![ + ExpectedService::Exit( + irrelevant_public_key.clone(), + incoming_route_d_wallet.clone(), + rate_pack_d, ), - host: Host::new("booga.com", TLS_PORT), - }) - .protocol(ProxyProtocol::TLS) - .build(), + ExpectedService::Routing( + irrelevant_public_key.clone(), + incoming_route_e_wallet.clone(), + rate_pack_e, + ), + ExpectedService::Routing( + irrelevant_public_key.clone(), + incoming_route_f_wallet.clone(), + rate_pack_f, + ), + ExpectedService::Nothing, + ], + protocol: ProxyProtocol::TLS, + hostname_opt: Some("server.com".to_string()), + }, ); let subject_addr: Addr = subject.start(); let dns_resolve_failure_payload = DnsResolveFailure_0v1::new(stream_key); @@ -4908,7 +4659,7 @@ mod tests { ExpiredCoresPackage::new( SocketAddr::from_str("1.2.3.4:1234").unwrap(), Some(make_wallet("irrelevant")), - return_route(cryptde), + return_route_with_id(cryptde, 1234), dns_resolve_failure_payload.into(), 0, ); @@ -4973,34 +4724,34 @@ mod tests { ); let stream_key = StreamKey::make_meaningless_stream_key(); let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); + let mut dns_failure_retries_hash_map = HashMap::new(); let client_payload = make_request_payload(111, cryptde); + dns_failure_retries_hash_map.insert( + stream_key, + DNSFailureRetry { + unsuccessful_request: client_payload, + retries_left: 0, + }, + ); subject.logger = Logger::new(test_name); + subject.dns_failure_retries = dns_failure_retries_hash_map; 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.stream_info.insert( - stream_key.clone(), - StreamInfoBuilder::new() - .dns_failure_retry(DNSFailureRetry { - unsuccessful_request: client_payload, - retries_left: 0, - }) - .route(RouteQueryResponse { - route: Route { hops: vec![] }, - expected_services: ExpectedServices::RoundTrip( - vec![], - vec![ExpectedService::Exit( - exit_public_key.clone(), - exit_wallet.clone(), - rate_pack(10), - )], - ), - host: Host::new("server.com", HTTP_PORT), - }) - .protocol(ProxyProtocol::HTTP) - .build(), + subject.route_ids_to_return_routes.insert( + 1234, + AddReturnRouteMessage { + return_route_id: 1234, + expected_services: vec![ExpectedService::Exit( + exit_public_key.clone(), + exit_wallet, + rate_pack(10), + )], + protocol: ProxyProtocol::HTTP, + hostname_opt: Some("server.com".to_string()), + }, ); let subject_addr: Addr = subject.start(); let dns_resolve_failure = DnsResolveFailure_0v1::new(stream_key); @@ -5008,7 +4759,7 @@ mod tests { ExpiredCoresPackage::new( SocketAddr::from_str("1.2.3.4:1234").unwrap(), Some(make_wallet("irrelevant")), - return_route(cryptde), + return_route_with_id(cryptde, 1234), dns_resolve_failure.into(), 0, ); @@ -5038,8 +4789,79 @@ mod tests { } #[test] - fn handle_dns_resolve_failure_logs_when_stream_key_is_found_in_stream_info_but_not_keys_and_addrs( + fn handle_dns_resolve_failure_does_not_send_message_to_neighborhood_when_server_is_not_specified( ) { + init_test_logging(); + let test_name = "handle_dns_resolve_failure_does_not_send_message_to_neighborhood_when_server_is_not_specified"; + let system = System::new(test_name); + let (neighborhood, _, neighborhood_recording_arc) = make_recorder(); + let cryptde = CRYPTDE_PAIR.main.as_ref(); + let mut subject = ProxyServer::new( + CRYPTDE_PAIR.clone(), + true, + Some(STANDARD_CONSUMING_WALLET_BALANCE), + false, + false, + ); + let stream_key = StreamKey::make_meaningless_stream_key(); + let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); + let mut dns_failure_retries_hash_map = HashMap::new(); + let client_payload = make_request_payload(111, cryptde); + dns_failure_retries_hash_map.insert( + stream_key, + DNSFailureRetry { + unsuccessful_request: client_payload, + retries_left: 0, + }, + ); + subject.logger = Logger::new(test_name); + subject.dns_failure_retries = dns_failure_retries_hash_map; + subject + .keys_and_addrs + .insert(stream_key.clone(), socket_addr); + let exit_public_key = PublicKey::from(&b"exit_key"[..]); + let exit_wallet = make_wallet("exit wallet"); + subject.route_ids_to_return_routes.insert( + 1234, + AddReturnRouteMessage { + return_route_id: 1234, + expected_services: vec![ExpectedService::Exit( + exit_public_key.clone(), + exit_wallet, + rate_pack(10), + )], + protocol: ProxyProtocol::HTTP, + hostname_opt: None, + }, + ); + 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(), + Some(make_wallet("irrelevant")), + return_route_with_id(cryptde, 1234), + dns_resolve_failure.into(), + 0, + ); + let peer_actors = peer_actors_builder().neighborhood(neighborhood).build(); + + 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_recording_arc.lock().unwrap(); + let record_opt = + neighborhood_recording.get_record_opt::(0); + assert_eq!(record_opt, None); + TestLogHandler::new().exists_log_containing(&format!( + "ERROR: {test_name}: Exit node {exit_public_key} complained of DNS failure, but was given no hostname to resolve." + )); + } + + #[test] + 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(); @@ -5052,39 +4874,43 @@ mod tests { false, ); let stream_key = StreamKey::make_meaningless_stream_key(); + let return_route_id = 1234; let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); + let mut dns_failure_retries_hash_map = HashMap::new(); let client_payload = make_request_payload(111, cryptde); + dns_failure_retries_hash_map.insert( + stream_key, + DNSFailureRetry { + unsuccessful_request: client_payload, + retries_left: 0, + }, + ); + subject.dns_failure_retries = dns_failure_retries_hash_map; + 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.stream_info.insert( - stream_key, - StreamInfoBuilder::new() - .dns_failure_retry(DNSFailureRetry { - unsuccessful_request: client_payload, - retries_left: 0, - }) - .route(RouteQueryResponse { - route: Route { hops: vec![] }, - expected_services: ExpectedServices::RoundTrip( - vec![], - vec![ExpectedService::Exit( - exit_public_key.clone(), - exit_wallet.clone(), - rate_pack(10), - )], - ), - host: Host::new("server.com", HTTP_PORT), - }) - .protocol(ProxyProtocol::HTTP) - .build(), + subject.route_ids_to_return_routes.insert( + return_route_id, + AddReturnRouteMessage { + return_route_id, + expected_services: vec![ExpectedService::Exit( + exit_public_key.clone(), + exit_wallet, + rate_pack(10), + )], + protocol: ProxyProtocol::HTTP, + hostname_opt: 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( - socket_addr, + SocketAddr::from_str("1.2.3.4:1234").unwrap(), Some(make_wallet("irrelevant")), - return_route(cryptde), + return_route_with_id(cryptde, return_route_id), dns_resolve_failure.into(), 0, ); @@ -5124,34 +4950,35 @@ mod tests { false, ); let stream_key = StreamKey::make_meaningless_stream_key(); + let return_route_id = 1234; let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); + let mut dns_failure_retries_hash_map = HashMap::new(); let client_payload = make_request_payload(111, cryptde); + dns_failure_retries_hash_map.insert( + stream_key, + DNSFailureRetry { + unsuccessful_request: client_payload, + retries_left: 0, + }, + ); + subject.dns_failure_retries = dns_failure_retries_hash_map; 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.stream_info.insert( - stream_key, - StreamInfoBuilder::new() - .dns_failure_retry(DNSFailureRetry { - unsuccessful_request: client_payload, - retries_left: 0, - }) - .route(RouteQueryResponse { - route: Route { hops: vec![] }, - expected_services: ExpectedServices::RoundTrip( - vec![], - vec![ExpectedService::Exit( - exit_public_key.clone(), - exit_wallet.clone(), - rate_pack(10), - )], - ), - host: Host::new("booga.com", HTTP_PORT), - }) - .protocol(ProxyProtocol::HTTP) - .build(), + subject.route_ids_to_return_routes.insert( + return_route_id, + AddReturnRouteMessage { + return_route_id, + expected_services: vec![ExpectedService::Exit( + exit_public_key.clone(), + exit_wallet, + rate_pack(10), + )], + protocol: ProxyProtocol::HTTP, + hostname_opt: None, + }, ); let subject_addr: Addr = subject.start(); let dns_resolve_failure = DnsResolveFailure_0v1::new(stream_key); @@ -5159,7 +4986,7 @@ mod tests { ExpiredCoresPackage::new( SocketAddr::from_str("1.2.3.4:1234").unwrap(), Some(make_wallet("irrelevant")), - return_route(cryptde), + return_route_with_id(cryptde, return_route_id), dns_resolve_failure.into(), 0, ); @@ -5176,10 +5003,12 @@ mod tests { System::current().stop(); system.run(); - TestLogHandler::new().exists_log_containing(&format!( - "Discarding DnsResolveFailure message from an unrecognized stream key {:?}", - stream_key - )); + TestLogHandler::new().exists_log_containing( + &format!( + "Discarding DnsResolveFailure message for from an unrecognized stream key {:?}", + stream_key + ) + ); } #[test] @@ -5204,38 +5033,47 @@ mod tests { subject.subs.as_mut().unwrap().dispatcher = peer_actors.dispatcher.from_dispatcher_client; let stream_key = StreamKey::make_meaningless_stream_key(); let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); + let mut dns_failure_retries_hash_map = HashMap::new(); let client_payload = make_request_payload(111, cryptde); + dns_failure_retries_hash_map.insert( + stream_key, + DNSFailureRetry { + unsuccessful_request: client_payload, + retries_left: 0, + }, + ); + subject.dns_failure_retries = dns_failure_retries_hash_map; subject .keys_and_addrs .insert(stream_key.clone(), socket_addr.clone()); - subject.stream_info.insert( - stream_key, - StreamInfoBuilder::new() - .dns_failure_retry(DNSFailureRetry { - unsuccessful_request: client_payload, - retries_left: 0, - }) - .tunneled_host("tunneled host") - .route(RouteQueryResponse { - route: Route { hops: vec![] }, - expected_services: ExpectedServices::RoundTrip( - vec![], - vec![ - make_exit_service_from_key(PublicKey::new(b"exit_node")), - ExpectedService::Nothing, - ], - ), - host: Host::new("booga.com", HTTP_PORT), - }) - .protocol(ProxyProtocol::HTTP) - .build(), + subject + .tunneled_hosts + .insert(stream_key.clone(), "tunneled host".to_string()); + subject.stream_key_routes.insert( + stream_key.clone(), + RouteQueryResponse { + route: Route { hops: vec![] }, + expected_services: ExpectedServices::OneWay(vec![]), + }, + ); + subject.route_ids_to_return_routes.insert( + 1234, + AddReturnRouteMessage { + return_route_id: 1234, + expected_services: vec![ + make_exit_service_from_key(PublicKey::new(b"exit_node")), + ExpectedService::Nothing, + ], + protocol: ProxyProtocol::HTTP, + hostname_opt: None, + }, ); 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(), Some(make_wallet("irrelevant")), - return_route(cryptde), + return_route_with_id(cryptde, 1234), dns_resolve_failure.into(), 0, ); @@ -5243,7 +5081,8 @@ mod tests { subject.handle_dns_resolve_failure(&expired_cores_package); assert!(subject.keys_and_addrs.is_empty()); - assert!(subject.stream_info.get(&stream_key).is_none()); + assert!(subject.stream_key_routes.is_empty()); + assert!(subject.tunneled_hosts.is_empty()); } #[test] @@ -5262,27 +5101,27 @@ mod tests { ); let stream_key = StreamKey::make_meaningless_stream_key(); let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); + let mut dns_failure_retries_hash_map = HashMap::new(); let client_payload = make_request_payload(111, cryptde); + dns_failure_retries_hash_map.insert( + stream_key, + DNSFailureRetry { + unsuccessful_request: client_payload, + retries_left: 0, + }, + ); + subject.dns_failure_retries = dns_failure_retries_hash_map; subject .keys_and_addrs .insert(stream_key.clone(), socket_addr.clone()); - subject.stream_info.insert( - stream_key.clone(), - StreamInfoBuilder::new() - .dns_failure_retry(DNSFailureRetry { - unsuccessful_request: client_payload, - retries_left: 0, - }) - .route(RouteQueryResponse { - route: Route { hops: vec![] }, - expected_services: ExpectedServices::RoundTrip( - vec![], - vec![ExpectedService::Nothing, ExpectedService::Nothing], - ), - host: Host::new("server.com", HTTP_PORT), - }) - .protocol(ProxyProtocol::HTTP) - .build(), + subject.route_ids_to_return_routes.insert( + 1234, + AddReturnRouteMessage { + return_route_id: 1234, + expected_services: vec![ExpectedService::Nothing, ExpectedService::Nothing], + protocol: ProxyProtocol::HTTP, + hostname_opt: Some("server.com".to_string()), + }, ); let subject_addr: Addr = subject.start(); let dns_resolve_failure = DnsResolveFailure_0v1::new(stream_key); @@ -5290,7 +5129,7 @@ mod tests { ExpiredCoresPackage::new( SocketAddr::from_str("1.2.3.4:1234").unwrap(), Some(make_wallet("irrelevant")), - return_route(cryptde), + return_route_with_id(cryptde, 1234), dns_resolve_failure.into(), 0, ); @@ -5321,9 +5160,9 @@ mod tests { TransmitDataMsg { endpoint: Endpoint::Socket(socket_addr), last_data: true, - sequence_number_opt: Some(0), + sequence_number: Some(0), data: ServerImpersonatorHttp {} - .dns_resolution_failure_response("server.com".to_string()), + .dns_resolution_failure_response(Some("server.com".to_string()),), }, *record ); @@ -5331,8 +5170,7 @@ mod tests { #[test] fn handle_dns_resolve_failure_sent_request_retry() { - let test_name = "handle_dns_resolve_failure_sent_request_retry"; - let system = System::new(test_name); + let system = System::new("test"); let resolve_message_params_arc = Arc::new(Mutex::new(vec![])); let (neighborhood_mock, _, _) = make_recorder(); let exit_public_key = PublicKey::from(&b"exit_key"[..]); @@ -5347,8 +5185,8 @@ mod tests { expected_services: ExpectedServices::RoundTrip( expected_services.clone(), expected_services.clone(), + 1234, ), - host: Host::new("booga.com", HTTP_PORT), }; let neighborhood_mock = neighborhood_mock .system_stop_conditions(match_every_type_id!(RouteQueryMessage)) @@ -5362,28 +5200,28 @@ mod tests { false, ); let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); + let mut dns_failure_retries_hash_map = HashMap::new(); let client_payload = make_request_payload(111, cryptde); let stream_key = client_payload.stream_key; + dns_failure_retries_hash_map.insert( + stream_key, + DNSFailureRetry { + unsuccessful_request: client_payload.clone(), + retries_left: 3, + }, + ); + subject.dns_failure_retries = dns_failure_retries_hash_map; subject .keys_and_addrs .insert(stream_key.clone(), socket_addr.clone()); - subject.stream_info.insert( - stream_key.clone(), - StreamInfoBuilder::new() - .dns_failure_retry(DNSFailureRetry { - unsuccessful_request: client_payload.clone(), - retries_left: 3, - }) - .route(RouteQueryResponse { - route: Route { hops: vec![] }, - expected_services: ExpectedServices::RoundTrip( - vec![], - expected_services.clone(), - ), - host: Host::new("server.com", HTTP_PORT), - }) - .protocol(ProxyProtocol::HTTP) - .build(), + subject.route_ids_to_return_routes.insert( + 1234, + AddReturnRouteMessage { + return_route_id: 1234, + expected_services: expected_services.clone(), + protocol: ProxyProtocol::HTTP, + hostname_opt: Some("server.com".to_string()), + }, ); let message_resolver = RouteQueryResponseResolverMock::default() .resolve_message_params(&resolve_message_params_arc); @@ -5398,7 +5236,7 @@ mod tests { ExpiredCoresPackage::new( SocketAddr::from_str("1.2.3.4:1234").unwrap(), Some(make_wallet("irrelevant")), - return_route(cryptde), + return_route_with_id(cryptde, 1234), dns_resolve_failure.into(), 0, ); @@ -5412,12 +5250,7 @@ mod tests { subject_addr .try_send(AssertionsMessage { assertions: Box::new(move |proxy_server: &mut ProxyServer| { - let retry = proxy_server - .stream_info(&stream_key) - .unwrap() - .dns_failure_retry_opt - .as_ref() - .unwrap(); + let retry = proxy_server.dns_failure_retries.get(&stream_key).unwrap(); assert_eq!(retry.retries_left, 2); }), }) @@ -5442,10 +5275,10 @@ mod tests { } #[test] - fn handle_dns_resolve_failure_logs_error_when_there_is_no_dns_failure_retry_entry_for_the_stream_key( + fn handle_dns_resolve_failure_logs_error_when_there_is_no_entry_in_the_hashmap_for_the_stream_key( ) { init_test_logging(); - let test_name = "handle_dns_resolve_failure_logs_error_when_there_is_no_dns_failure_retry_entry_for_the_stream_key"; + let test_name = "handle_dns_resolve_failure_logs_error_when_there_is_no_entry_in_the_hashmap_for_the_stream_key"; let system = System::new(test_name); let exit_public_key = PublicKey::from(&b"exit_key"[..]); let exit_wallet = make_wallet("exit wallet"); @@ -5468,19 +5301,14 @@ mod tests { subject .keys_and_addrs .insert(stream_key.clone(), socket_addr.clone()); - subject.stream_info.insert( - stream_key.clone(), - StreamInfoBuilder::new() - .route(RouteQueryResponse { - route: Route { hops: vec![] }, - expected_services: ExpectedServices::RoundTrip( - vec![], - expected_services.clone(), - ), - host: Host::new("server.com", HTTP_PORT), - }) - .protocol(ProxyProtocol::HTTP) - .build(), + subject.route_ids_to_return_routes.insert( + 1234, + AddReturnRouteMessage { + return_route_id: 1234, + expected_services: expected_services.clone(), + protocol: ProxyProtocol::HTTP, + hostname_opt: Some("server.com".to_string()), + }, ); let subject_addr: Addr = subject.start(); let dns_resolve_failure = DnsResolveFailure_0v1::new(stream_key); @@ -5488,7 +5316,7 @@ mod tests { ExpiredCoresPackage::new( SocketAddr::from_str("1.2.3.4:1234").unwrap(), Some(make_wallet("irrelevant")), - return_route(cryptde), + return_route_with_id(cryptde, 1234), dns_resolve_failure.into(), 0, ); @@ -5501,7 +5329,8 @@ mod tests { system.run(); TestLogHandler::new().exists_log_containing(&format!( "ERROR: {test_name}: While \ - handling ExpiredCoresPackage: No DNSFailureRetry entry found for the stream_key: {stream_key}" + handling ExpiredCoresPackage: No entry found inside dns_failure_retries hashmap for \ + the stream_key: AAAAAAAAAAAAAAAAAAAAAAAAAAA" )); } @@ -5524,8 +5353,8 @@ mod tests { expected_services: ExpectedServices::RoundTrip( expected_services.clone(), expected_services.clone(), + 1234, ), - host: Host::new("booga.com", HTTP_PORT), }; let neighborhood_mock = neighborhood_mock .system_stop_conditions(match_every_type_id!( @@ -5544,29 +5373,29 @@ mod tests { ); subject.logger = Logger::new(test_name); let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); + let mut dns_failure_retries_hash_map = HashMap::new(); let client_payload = make_request_payload(111, cryptde); let stream_key = client_payload.stream_key; let stream_key_clone = stream_key.clone(); + dns_failure_retries_hash_map.insert( + stream_key, + DNSFailureRetry { + unsuccessful_request: client_payload.clone(), + retries_left: 3, + }, + ); + subject.dns_failure_retries = dns_failure_retries_hash_map; subject .keys_and_addrs .insert(stream_key.clone(), socket_addr.clone()); - subject.stream_info.insert( - stream_key_clone.clone(), - StreamInfoBuilder::new() - .dns_failure_retry(DNSFailureRetry { - unsuccessful_request: client_payload, - retries_left: 3, - }) - .route(RouteQueryResponse { - route: Route { hops: vec![] }, - expected_services: ExpectedServices::RoundTrip( - vec![], - expected_services.clone(), - ), - host: Host::new("server.com", HTTP_PORT), - }) - .protocol(ProxyProtocol::HTTP) - .build(), + subject.route_ids_to_return_routes.insert( + 1234, + AddReturnRouteMessage { + return_route_id: 1234, + expected_services: expected_services.clone(), + protocol: ProxyProtocol::HTTP, + hostname_opt: Some("server.com".to_string()), + }, ); let message_resolver_factory = RouteQueryResponseResolverFactoryMock::default() .make_params(&make_params_arc) @@ -5583,7 +5412,7 @@ mod tests { ExpiredCoresPackage::new( SocketAddr::from_str("1.2.3.4:1234").unwrap(), Some(make_wallet("irrelevant")), - return_route(cryptde), + return_route_with_id(cryptde, 1234), dns_resolve_failure.into(), 0, ); @@ -5607,7 +5436,9 @@ mod tests { .try_send(AssertionsMessage { assertions: Box::new(move |proxy_server: &mut ProxyServer| { assert_eq!(proxy_server.keys_and_addrs.a_to_b(&stream_key), None); - assert_eq!(proxy_server.stream_info.get(&stream_key).is_none(), true); + assert_eq!(proxy_server.stream_key_routes.get(&stream_key), None); + assert_eq!(proxy_server.tunneled_hosts.get(&stream_key), None); + assert_eq!(proxy_server.dns_failure_retries.get(&stream_key), None); }), }) .unwrap(); @@ -5636,20 +5467,15 @@ mod tests { subject .keys_and_addrs .insert(stream_key.clone(), socket_addr.clone()); - let remaining_route = return_route(cryptde); - subject.stream_info.insert( - stream_key.clone(), - StreamInfoBuilder::new() - .route(RouteQueryResponse { - route: Route { hops: vec![] }, - expected_services: ExpectedServices::RoundTrip( - vec![], - vec![ExpectedService::Nothing], - ), - host: Host::new("booga.com", HTTP_PORT), - }) - .protocol(ProxyProtocol::HTTP) - .build(), + let remaining_route = return_route_with_id(cryptde, 4321); + subject.route_ids_to_return_routes.insert( + 4321, + AddReturnRouteMessage { + return_route_id: 4321, + expected_services: vec![ExpectedService::Nothing], + protocol: ProxyProtocol::HTTP, + hostname_opt: None, + }, ); let subject_addr: Addr = subject.start(); @@ -5692,8 +5518,8 @@ mod tests { let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr.clone(), - reception_port_opt: Some(80), - sequence_number_opt: Some(0), + reception_port: Some(80), + sequence_number: Some(0), last_data: false, is_clandestine: false, data: expected_data.clone(), @@ -5731,7 +5557,57 @@ mod tests { .accountant(accountant) .build(); let client_response_payload = ClientResponsePayload_0v1 { - stream_key: stream_key.clone(), + stream_key, + sequenced_packet: SequencedPacket { + data: b"some data".to_vec(), + sequence_number: 4321, + last_data: false, + }, + }; + let expired_cores_package = ExpiredCoresPackage::new( + SocketAddr::from_str("1.2.3.4:1234").unwrap(), + Some(make_wallet("irrelevant")), + return_route_with_id(cryptde, 1234), + client_response_payload, + 0, + ); + subject_addr.try_send(BindMessage { peer_actors }).unwrap(); + + subject_addr.try_send(expired_cores_package).unwrap(); + + System::current().stop(); + system.run(); + TestLogHandler::new().exists_log_containing("ERROR: ProxyServer: Can't report services consumed: received response with bogus return-route ID 1234 for client response. Ignoring"); + assert_eq!(dispatcher_recording_arc.lock().unwrap().len(), 0); + assert_eq!(accountant_recording_arc.lock().unwrap().len(), 0); + } + + #[test] + fn report_response_services_consumed_complains_and_drops_package_if_return_route_id_is_unreadable( + ) { + init_test_logging(); + let cryptde = CRYPTDE_PAIR.main.as_ref(); + let (dispatcher, _, dispatcher_recording_arc) = make_recorder(); + let (accountant, _, accountant_recording_arc) = make_recorder(); + let system = System::new("report_response_services_consumed_complains_and_drops_package_if_return_route_id_is_unreadable"); + let mut subject = ProxyServer::new( + CRYPTDE_PAIR.clone(), + true, + Some(STANDARD_CONSUMING_WALLET_BALANCE), + false, + false, + ); + let stream_key = StreamKey::make_meaningless_stream_key(); + subject + .keys_and_addrs + .insert(stream_key, SocketAddr::from_str("1.2.3.4:5678").unwrap()); + let subject_addr: Addr = subject.start(); + let peer_actors = peer_actors_builder() + .dispatcher(dispatcher) + .accountant(accountant) + .build(); + let client_response_payload = ClientResponsePayload_0v1 { + stream_key, sequenced_packet: SequencedPacket { data: b"some data".to_vec(), sequence_number: 4321, @@ -5741,7 +5617,9 @@ mod tests { let expired_cores_package = ExpiredCoresPackage::new( SocketAddr::from_str("1.2.3.4:1234").unwrap(), Some(make_wallet("irrelevant")), - return_route(cryptde), + Route { + hops: vec![make_cover_hop(cryptde), CryptData::new(&[0])], + }, client_response_payload, 0, ); @@ -5751,11 +5629,74 @@ mod tests { System::current().stop(); system.run(); - TestLogHandler::new().exists_log_containing(format!("ERROR: ProxyServer: Can't pay for return services consumed: received response with unrecognized stream key {}. Ignoring", stream_key).as_str()); + TestLogHandler::new().exists_log_containing( + "ERROR: ProxyServer: Can't report services consumed: DecryptionError(InvalidKey(\"Could not decrypt with", + ); assert_eq!(dispatcher_recording_arc.lock().unwrap().len(), 0); assert_eq!(accountant_recording_arc.lock().unwrap().len(), 0); } + #[test] + fn return_route_ids_expire_when_instructed() { + init_test_logging(); + let cryptde = CRYPTDE_PAIR.main.as_ref(); + let stream_key = StreamKey::make_meaningless_stream_key(); + + let (tx, rx) = unbounded(); + thread::spawn(move || { + let system = System::new("report_response_services_consumed_complains_and_drops_package_if_return_route_id_does_not_exist"); + let mut subject = ProxyServer::new( + CRYPTDE_PAIR.clone(), + true, + Some(STANDARD_CONSUMING_WALLET_BALANCE), + false, + false, + ); + subject.route_ids_to_return_routes = TtlHashMap::new(Duration::from_millis(250)); + subject + .keys_and_addrs + .insert(stream_key, SocketAddr::from_str("1.2.3.4:5678").unwrap()); + subject.route_ids_to_return_routes.insert( + 1234, + AddReturnRouteMessage { + return_route_id: 1234, + expected_services: vec![], + protocol: ProxyProtocol::TLS, + hostname_opt: None, + }, + ); + let subject_addr: Addr = subject.start(); + let peer_actors = peer_actors_builder().build(); + subject_addr.try_send(BindMessage { peer_actors }).unwrap(); + tx.send(subject_addr).unwrap(); + + system.run(); + }); + + let subject_addr = rx.recv().unwrap(); + + thread::sleep(Duration::from_millis(300)); + + let client_response_payload = ClientResponsePayload_0v1 { + stream_key, + sequenced_packet: SequencedPacket { + data: b"some data".to_vec(), + sequence_number: 4321, + last_data: false, + }, + }; + let expired_cores_package = ExpiredCoresPackage::new( + SocketAddr::from_str("1.2.3.4:1234").unwrap(), + Some(make_wallet("irrelevant")), + return_route_with_id(cryptde, 1234), + client_response_payload, + 0, + ); + subject_addr.try_send(expired_cores_package).unwrap(); + + TestLogHandler::new().await_log_containing("ERROR: ProxyServer: Can't report services consumed: received response with bogus return-route ID 1234 for client response. Ignoring", 1000); + } + #[test] fn handle_stream_shutdown_msg_handles_unknown_peer_addr() { let mut subject = ProxyServer::new(CRYPTDE_PAIR.clone(), true, None, false, false); @@ -5764,17 +5705,16 @@ mod tests { subject .keys_and_addrs .insert(unaffected_stream_key, unaffected_socket_addr); - subject.stream_info.insert( + subject.stream_key_routes.insert( unaffected_stream_key, - StreamInfoBuilder::new() - .route(RouteQueryResponse { - route: Route { hops: vec![] }, - expected_services: ExpectedServices::RoundTrip(vec![], vec![]), - host: Host::new("booga.com", HTTP_PORT), - }) - .tunneled_host("blah") - .build(), + RouteQueryResponse { + route: Route { hops: vec![] }, + expected_services: ExpectedServices::RoundTrip(vec![], vec![], 1234), + }, ); + subject + .tunneled_hosts + .insert(unaffected_stream_key, "blah".to_string()); subject.handle_stream_shutdown_msg(StreamShutdownMsg { peer_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), @@ -5790,12 +5730,10 @@ mod tests { .keys_and_addrs .a_to_b(&unaffected_stream_key) .is_some()); - assert!(subject.stream_info.contains_key(&unaffected_stream_key)); assert!(subject - .stream_info(&unaffected_stream_key) - .unwrap() - .tunneled_host_opt - .is_some()); + .stream_key_routes + .contains_key(&unaffected_stream_key)); + assert!(subject.tunneled_hosts.contains_key(&unaffected_stream_key)); } #[test] @@ -5819,16 +5757,12 @@ mod tests { subject .keys_and_addrs .insert(affected_stream_key, affected_socket_addr); - subject.stream_info.insert( + subject.stream_key_routes.insert( unaffected_stream_key, - StreamInfoBuilder::new() - .route(RouteQueryResponse { - route: Route { hops: vec![] }, - expected_services: ExpectedServices::RoundTrip(vec![], vec![]), - host: Host::new("gooba.com", HTTP_PORT), - }) - .tunneled_host("blah") - .build(), + RouteQueryResponse { + route: Route { hops: vec![] }, + expected_services: ExpectedServices::RoundTrip(vec![], vec![], 1234), + }, ); let affected_route = Route::round_trip( RouteSegment::new( @@ -5847,6 +5781,7 @@ mod tests { ), CRYPTDE_PAIR.main.as_ref(), Some(make_paying_wallet(b"consuming")), + 1234, Some(TEST_DEFAULT_CHAIN.rec().contract), ) .unwrap(); @@ -5855,20 +5790,23 @@ mod tests { make_paying_wallet(b"1234"), DEFAULT_RATE_PACK, )]; - subject.stream_info.insert( + subject.stream_key_routes.insert( affected_stream_key, - StreamInfoBuilder::new() - .route(RouteQueryResponse { - route: affected_route.clone(), - expected_services: ExpectedServices::RoundTrip( - affected_expected_services, - vec![], - ), - host: Host::new("gooba.com", TLS_PORT), - }) - .tunneled_host("tunneled.com") - .build(), + RouteQueryResponse { + route: affected_route.clone(), + expected_services: ExpectedServices::RoundTrip( + affected_expected_services, + vec![], + 1234, + ), + }, ); + subject + .tunneled_hosts + .insert(unaffected_stream_key, "blah".to_string()); + subject + .tunneled_hosts + .insert(affected_stream_key, "tunneled.com".to_string()); let subject_addr = subject.start(); let (hopper, _, hopper_recording_arc) = make_recorder(); let (proxy_server, _, proxy_server_recording_arc) = make_recorder(); @@ -5902,8 +5840,8 @@ mod tests { ClientRequestPayload_0v1 { stream_key: affected_stream_key, sequenced_packet: SequencedPacket::new(vec![], 1234, true), - target_hostname: String::from("tunneled.com"), - target_port: TLS_PORT, + target_hostname: Some(String::from("tunneled.com")), + target_port: 443, protocol: ProxyProtocol::TLS, originator_public_key: CRYPTDE_PAIR.alias.as_ref().public_key().clone(), } @@ -5949,15 +5887,12 @@ mod tests { subject .keys_and_addrs .insert(affected_stream_key, affected_socket_addr); - subject.stream_info.insert( + subject.stream_key_routes.insert( unaffected_stream_key, - StreamInfoBuilder::new() - .route(RouteQueryResponse { - route: Route { hops: vec![] }, - expected_services: ExpectedServices::RoundTrip(vec![], vec![]), - host: Host::new("booga.com", HTTP_PORT), - }) - .build(), + RouteQueryResponse { + route: Route { hops: vec![] }, + expected_services: ExpectedServices::RoundTrip(vec![], vec![], 1234), + }, ); let affected_route = Route::round_trip( RouteSegment::new( @@ -5976,6 +5911,7 @@ mod tests { ), CRYPTDE_PAIR.main.as_ref(), Some(make_paying_wallet(b"consuming")), + 1234, Some(TEST_DEFAULT_CHAIN.rec().contract), ) .unwrap(); @@ -5984,18 +5920,16 @@ mod tests { make_paying_wallet(b"1234"), DEFAULT_RATE_PACK, )]; - subject.stream_info.insert( + subject.stream_key_routes.insert( affected_stream_key, - StreamInfoBuilder::new() - .route(RouteQueryResponse { - route: affected_route.clone(), - expected_services: ExpectedServices::RoundTrip( - affected_expected_services, - vec![], - ), - host: Host::new("booga.com", HTTP_PORT), - }) - .build(), + RouteQueryResponse { + route: affected_route.clone(), + expected_services: ExpectedServices::RoundTrip( + affected_expected_services, + vec![], + 1234, + ), + }, ); subject.logger = Logger::new(test_name); let subject_addr = subject.start(); @@ -6031,7 +5965,7 @@ mod tests { ClientRequestPayload_0v1 { stream_key: affected_stream_key, sequenced_packet: SequencedPacket::new(vec![], 1234, true), - target_hostname: "booga.com".to_string(), + target_hostname: None, target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: CRYPTDE_PAIR.alias.as_ref().public_key().clone(), @@ -6070,9 +6004,6 @@ mod tests { let socket_addr = SocketAddr::from_str("3.4.5.6:7777").unwrap(); let stream_key = StreamKey::make_meaningful_stream_key("All Things Must Pass"); subject.keys_and_addrs.insert(stream_key, socket_addr); - subject - .stream_info - .insert(stream_key.clone(), StreamInfoBuilder::new().build()); let msg = StreamShutdownMsg { peer_addr: socket_addr, stream_type: RemovedStreamType::NonClandestine(NonClandestineAttributes { @@ -6099,17 +6030,16 @@ mod tests { let socket_addr = SocketAddr::from_str("3.4.5.6:7890").unwrap(); let stream_key = StreamKey::make_meaningful_stream_key("All Things Must Pass"); subject.keys_and_addrs.insert(stream_key, socket_addr); - subject.stream_info.insert( + subject.stream_key_routes.insert( stream_key, - StreamInfoBuilder::new() - .route(RouteQueryResponse { - route: Route { hops: vec![] }, - expected_services: ExpectedServices::RoundTrip(vec![], vec![]), - host: Host::new("booga.com", HTTP_PORT), - }) - .tunneled_host("blah") - .build(), + 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 { @@ -6125,13 +6055,14 @@ mod tests { let after = SystemTime::now(); let handle_normal_client_data = help_to_handle_normal_client_data_params_arc.lock().unwrap(); - let inbound_client_data_msg = &handle_normal_client_data[0]; + let (inbound_client_data_msg, retire_stream_key) = &handle_normal_client_data[0]; assert_eq!(inbound_client_data_msg.client_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] @@ -6141,15 +6072,18 @@ mod tests { let inbound_client_data_msg = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:4578").unwrap(), - reception_port_opt: None, + reception_port: None, last_data: true, is_clandestine: false, - sequence_number_opt: Some(123), + sequence_number: Some(123), data: vec![], }; - let result = IBCDHelperReal::new() - .handle_normal_client_data(&mut proxy_server, inbound_client_data_msg); + let result = IBCDHelperReal::new().handle_normal_client_data( + &mut proxy_server, + inbound_client_data_msg, + true, + ); assert_eq!( result, @@ -6177,10 +6111,16 @@ mod tests { accountant_sub: recipient!(&addr, ReportServicesConsumedMessage), retire_stream_key_sub_opt: None, }; + let add_return_route_sub = recipient!(&addr, AddReturnRouteMessage); let subject = RouteQueryResponseResolverReal {}; let system = System::new("resolve_message_handles_mailbox_error_from_neighborhood"); - subject.resolve_message(args, proxy_server_sub, Err(MailboxError::Timeout)); + subject.resolve_message( + args, + add_return_route_sub, + proxy_server_sub, + Err(MailboxError::Timeout), + ); System::current().stop(); system.run(); @@ -6198,63 +6138,22 @@ mod tests { #[derive(Default)] struct ClientRequestPayloadFactoryMock { - make_params: Arc< - Mutex< - Vec<( - InboundClientData, - StreamKey, - Option, - Box, - Logger, - )>, - >, - >, make_results: RefCell>>, } impl ClientRequestPayloadFactory for ClientRequestPayloadFactoryMock { fn make( &self, - ibcd: &InboundClientData, - stream_key: StreamKey, - host_opt: Option, - cryptde: &dyn CryptDE, - logger: &Logger, + _ibcd: &InboundClientData, + _stream_key: StreamKey, + _cryptde: &dyn CryptDE, + _logger: &Logger, ) -> Option { - self.make_params.lock().unwrap().push(( - ibcd.clone(), - stream_key, - host_opt, - cryptde.dup(), - logger.clone(), - )); self.make_results.borrow_mut().remove(0) } } impl ClientRequestPayloadFactoryMock { - fn new() -> Self { - Self::default() - } - - fn make_params( - mut self, - params: &Arc< - Mutex< - Vec<( - InboundClientData, - StreamKey, - Option, - Box, - Logger, - )>, - >, - >, - ) -> Self { - self.make_params = params.clone(); - self - } - fn make_result(self, result: Option) -> Self { self.make_results.borrow_mut().push(result); self @@ -6276,15 +6175,18 @@ mod tests { let inbound_client_data_msg = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:4578").unwrap(), - reception_port_opt: Some(568), + reception_port: Some(568), last_data: true, is_clandestine: false, - sequence_number_opt: Some(123), + sequence_number: Some(123), data: vec![], }; - let result = IBCDHelperReal::new() - .handle_normal_client_data(&mut proxy_server, inbound_client_data_msg); + let result = IBCDHelperReal::new().handle_normal_client_data( + &mut proxy_server, + inbound_client_data_msg, + true, + ); assert_eq!( result, @@ -6294,7 +6196,7 @@ mod tests { #[test] fn new_http_request_creates_new_entry_inside_dns_retries_hashmap() { - let test_name = "new_http_request_creates_new_entry_inside_dns_retries_hashmap"; + let alias_cryptde = CRYPTDE_PAIR.alias.as_ref(); let http_request = b"GET /index.html HTTP/1.1\r\nHost: nowhere.com\r\n\r\n"; let (neighborhood_mock, _, _) = make_recorder(); let destination_key = PublicKey::from(&b"our destination"[..]); @@ -6303,8 +6205,8 @@ mod tests { expected_services: ExpectedServices::RoundTrip( vec![make_exit_service_from_key(destination_key.clone())], vec![], + 1234, ), - host: Host::new("booga.com", HTTP_PORT), })); let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); let stream_key = StreamKey::make_meaningless_stream_key(); @@ -6312,8 +6214,8 @@ mod tests { let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr.clone(), - reception_port_opt: Some(HTTP_PORT), - sequence_number_opt: Some(0), + reception_port: Some(HTTP_PORT), + sequence_number: Some(0), last_data: true, is_clandestine: false, data: expected_data.clone(), @@ -6322,13 +6224,14 @@ mod tests { .make( &msg_from_dispatcher, stream_key.clone(), - None, - CRYPTDE_PAIR.alias.as_ref(), + alias_cryptde, &Logger::new("test"), ) .unwrap(); let stream_key_factory = StreamKeyFactoryMock::new().make_result(stream_key.clone()); - let system = System::new(test_name); + let system = System::new( + "proxy_server_receives_http_request_from_dispatcher_then_sends_cores_package_to_hopper", + ); let mut subject = ProxyServer::new( CRYPTDE_PAIR.clone(), true, @@ -6348,12 +6251,7 @@ mod tests { subject_addr .try_send(AssertionsMessage { assertions: Box::new(move |proxy_server: &mut ProxyServer| { - let dns_retry = proxy_server - .stream_info(&stream_key) - .unwrap() - .dns_failure_retry_opt - .as_ref() - .unwrap(); + let dns_retry = proxy_server.dns_failure_retries.get(&stream_key).unwrap(); assert_eq!(dns_retry.retries_left, 3); assert_eq!(dns_retry.unsuccessful_request, expected_payload); }), @@ -6365,8 +6263,6 @@ mod tests { #[test] fn new_http_request_creates_new_exhausted_entry_inside_dns_retries_hashmap_zero_hop() { - let test_name = - "new_http_request_creates_new_exhausted_entry_inside_dns_retries_hashmap_zero_hop"; let http_request = b"GET /index.html HTTP/1.1\r\nHost: nowhere.com\r\n\r\n"; let (neighborhood_mock, _, _) = make_recorder(); let destination_key = PublicKey::from(&b"our destination"[..]); @@ -6375,8 +6271,8 @@ mod tests { expected_services: ExpectedServices::RoundTrip( vec![make_exit_service_from_key(destination_key.clone())], vec![], + 1234, ), - host: Host::new("booga.com", HTTP_PORT), })); let socket_addr = SocketAddr::from_str("1.2.3.4:5678").unwrap(); let stream_key = StreamKey::make_meaningless_stream_key(); @@ -6384,8 +6280,8 @@ mod tests { let msg_from_dispatcher = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr.clone(), - reception_port_opt: Some(HTTP_PORT), - sequence_number_opt: Some(0), + reception_port: Some(HTTP_PORT), + sequence_number: Some(0), last_data: true, is_clandestine: false, data: expected_data.clone(), @@ -6394,13 +6290,14 @@ mod tests { .make( &msg_from_dispatcher, stream_key.clone(), - None, CRYPTDE_PAIR.alias.as_ref(), &Logger::new("test"), ) .unwrap(); let stream_key_factory = StreamKeyFactoryMock::new().make_result(stream_key.clone()); - let system = System::new(test_name); + let system = System::new( + "new_http_request_creates_new_exhausted_entry_inside_dns_retries_hashmap_zero_hop", + ); let mut subject = ProxyServer::new( CRYPTDE_PAIR.clone(), false, @@ -6420,12 +6317,7 @@ mod tests { subject_addr .try_send(AssertionsMessage { assertions: Box::new(move |proxy_server: &mut ProxyServer| { - let dns_retry = proxy_server - .stream_info(&stream_key) - .unwrap() - .dns_failure_retry_opt - .as_ref() - .unwrap(); + let dns_retry = proxy_server.dns_failure_retries.get(&stream_key).unwrap(); assert_eq!(dns_retry.retries_left, 0); assert_eq!(dns_retry.unsuccessful_request, expected_payload); }), @@ -6515,15 +6407,18 @@ mod tests { let inbound_client_data_msg = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.2.3.4:4578").unwrap(), - reception_port_opt: Some(80), + reception_port: Some(80), last_data: true, is_clandestine: false, - sequence_number_opt: Some(123), + sequence_number: Some(123), data: expected_data, }; - let result = IBCDHelperReal::new() - .handle_normal_client_data(&mut proxy_server, inbound_client_data_msg); + let result = IBCDHelperReal::new().handle_normal_client_data( + &mut proxy_server, + inbound_client_data_msg, + true, + ); assert_eq!( result, @@ -6531,78 +6426,6 @@ mod tests { ); } - #[test] - fn make_payload_passes_no_hostname_if_none_is_known() { - let mut subject = ProxyServer::new(CRYPTDE_PAIR.clone(), true, Some(58), false, false); - let make_params_arc = Arc::new(Mutex::new(vec![])); - let client_request_payload_factory = ClientRequestPayloadFactoryMock::new() - .make_params(&make_params_arc) - .make_result(None); - subject.client_request_payload_factory = Box::new(client_request_payload_factory); - let stream_key = StreamKey::make_meaningless_stream_key(); - // Do not create an entry in subject.stream_info for stream_key, so that no hostname is known - - let _ = subject.make_payload( - InboundClientData { - // irrelevant - timestamp: SystemTime::now(), - client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port_opt: Some(HTTP_PORT), - last_data: false, - is_clandestine: false, - sequence_number_opt: Some(123), - data: vec![], - }, - &stream_key, - ); - - let (_ibcd, _sk, hostname_opt, _cryptde, _logger) = &make_params_arc.lock().unwrap()[0]; - assert_eq!(hostname_opt, &None); - } - - #[test] - fn make_payload_passes_hostname_if_known() { - let mut subject = ProxyServer::new(CRYPTDE_PAIR.clone(), true, Some(58), false, false); - let make_params_arc = Arc::new(Mutex::new(vec![])); - let client_request_payload_factory = ClientRequestPayloadFactoryMock::new() - .make_params(&make_params_arc) - .make_result(None); // Don't care about return value, only parameters - subject.client_request_payload_factory = Box::new(client_request_payload_factory); - let stream_key = StreamKey::make_meaningless_stream_key(); - let si_host = Host::new("knownhostname.com", 2345); - subject.stream_info.insert( - stream_key.clone(), - StreamInfo { - tunneled_host_opt: None, - dns_failure_retry_opt: None, - route_opt: Some(RouteQueryResponse { - route: Route { hops: vec![] }, - expected_services: ExpectedServices::RoundTrip(vec![], vec![]), - host: Host::new(&si_host.name, 2345), - }), - protocol_opt: None, - time_to_live_opt: None, - }, - ); - - let _ = subject.make_payload( - InboundClientData { - // irrelevant - timestamp: SystemTime::now(), - client_addr: SocketAddr::from_str("1.2.3.4:5678").unwrap(), - reception_port_opt: Some(HTTP_PORT), - last_data: false, - is_clandestine: false, - sequence_number_opt: Some(123), - data: vec![], - }, - &stream_key, - ); - - let (_ibcd, _sk, host_opt, _cryptde, _logger) = &make_params_arc.lock().unwrap()[0]; - assert_eq!(host_opt, &Some(si_host)); - } - #[test] #[should_panic( expected = "ProxyServer should never get ShutdownStreamMsg about clandestine stream" @@ -6643,10 +6466,10 @@ mod tests { let ibcd = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr, - reception_port_opt: Some(2222), + reception_port: Some(2222), last_data: true, is_clandestine: false, - sequence_number_opt: Some(333), + sequence_number: Some(333), data: b"GET /index.html HTTP/1.1\r\nHost: header.com:3333\r\n\r\n".to_vec(), }; @@ -6667,10 +6490,10 @@ mod tests { let ibcd = InboundClientData { timestamp: SystemTime::now(), client_addr: socket_addr, - reception_port_opt: Some(2222), + reception_port: Some(2222), last_data: true, is_clandestine: false, - sequence_number_opt: Some(333), + sequence_number: Some(333), data: b"GET /index.html HTTP/1.1\r\nHost: header.com:4444\r\n\r\n".to_vec(), }; @@ -6683,30 +6506,6 @@ mod tests { ); } - fn make_server_com_client_hello() -> Vec { - [ - 0x16, // content_type: Handshake - 0x00, 0x00, 0x00, 0x00, // version, length: don't care - 0x01, // handshake_type: ClientHello - 0x00, 0x00, 0x00, 0x00, 0x00, // length, version: don't care - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, // random: don't care - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, // random: don't care - 0x00, // session_id_length - 0x00, 0x00, // cipher_suites_length - 0x00, // compression_methods_length - 0x00, 0x13, // extensions_length - 0x00, 0x00, // extension_type: server_name - 0x00, 0x0F, // extension_length - 0x00, 0x0D, // server_name_list_length - 0x00, // server_name_type - 0x00, 0x0A, // server_name_length - b's', b'e', b'r', b'v', b'e', b'r', b'.', b'c', b'o', b'm', // server_name - ] - .to_vec() - } - 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 e5607bb08..9697ce6a2 100644 --- a/node/src/proxy_server/protocol_pack.rs +++ b/node/src/proxy_server/protocol_pack.rs @@ -3,16 +3,29 @@ use crate::proxy_server::http_protocol_pack::HttpProtocolPack; use crate::proxy_server::tls_protocol_pack::TlsProtocolPack; use crate::sub_lib::cryptde::PlainData; use crate::sub_lib::dispatcher::InboundClientData; -use crate::sub_lib::host::Host; use crate::sub_lib::proxy_server::ProxyProtocol; use masq_lib::constants::{HTTP_PORT, TLS_PORT}; +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Host { + pub name: String, + pub port: u16, +} + +impl Host { + pub fn new(name: &str, port: u16) -> Host { + Host { + name: name.to_string(), + port, + } + } +} + pub trait ProtocolPack: Send + Sync { fn proxy_protocol(&self) -> ProxyProtocol; fn standard_port(&self) -> u16; fn find_host(&self, data: &PlainData) -> Option; fn server_impersonator(&self) -> Box; - fn describe_packet(&self, data: &PlainData) -> String; } pub fn from_protocol(protocol: ProxyProtocol) -> Box { @@ -31,7 +44,7 @@ pub fn from_standard_port(standard_port: u16) -> Option> { } pub fn from_ibcd(ibcd: &InboundClientData) -> Result, String> { - let origin_port = match ibcd.reception_port_opt { + let origin_port = match ibcd.reception_port { None => { return Err(format!( "No origin port specified with {}-byte non-clandestine packet: {:?}", @@ -54,6 +67,6 @@ pub fn from_ibcd(ibcd: &InboundClientData) -> Result, Stri pub trait ServerImpersonator { fn route_query_failure_response(&self, server_name: &str) -> Vec; - fn dns_resolution_failure_response(&self, server_name: String) -> Vec; + fn dns_resolution_failure_response(&self, server_name_opt: Option) -> Vec; fn consuming_wallet_absent(&self) -> Vec; } diff --git a/node/src/proxy_server/server_impersonator_http.rs b/node/src/proxy_server/server_impersonator_http.rs index 9de02301d..554741aae 100644 --- a/node/src/proxy_server/server_impersonator_http.rs +++ b/node/src/proxy_server/server_impersonator_http.rs @@ -19,9 +19,11 @@ impl ServerImpersonator for ServerImpersonatorHttp { ) } - fn dns_resolution_failure_response(&self, server_name: String) -> Vec { - let (server_name, quoted_server_name) = - (server_name.clone(), format!("\"{}\"", server_name)); + fn dns_resolution_failure_response(&self, server_name_opt: Option) -> Vec { + let (server_name, quoted_server_name) = match &server_name_opt { + Some(name) => (name.clone(), format!("\"{}\"", name)), + None => ("".to_string(), "".to_string()), + }; ServerImpersonatorHttp::make_error_response( 503, "DNS Resolution Problem", @@ -195,7 +197,7 @@ mod tests { fn dns_resolution_failure_response_with_server_name_produces_expected_error_page() { let subject = ServerImpersonatorHttp {}; - let result = subject.dns_resolution_failure_response("server.com".to_string()); + let result = subject.dns_resolution_failure_response(Some("server.com".to_string())); let expected = ServerImpersonatorHttp::make_error_response( 503, @@ -206,6 +208,21 @@ mod tests { assert_eq!(expected, result); } + #[test] + fn dns_resolution_failure_response_without_server_name_produces_expected_error_page() { + let subject = ServerImpersonatorHttp {}; + + let result = subject.dns_resolution_failure_response(None); + + let expected = ServerImpersonatorHttp::make_error_response( + 503, + "DNS Resolution Problem", + "Exit Nodes couldn't resolve ", + "DNS Failure, We have tried multiple Exit Nodes and all have failed to resolve this address ", + ); + assert_eq!(expected, result); + } + #[test] fn consuming_wallet_absent_response_produces_expected_error_page() { let subject = ServerImpersonatorHttp {}; diff --git a/node/src/proxy_server/server_impersonator_tls.rs b/node/src/proxy_server/server_impersonator_tls.rs index 3312be656..7a193a269 100644 --- a/node/src/proxy_server/server_impersonator_tls.rs +++ b/node/src/proxy_server/server_impersonator_tls.rs @@ -8,7 +8,7 @@ impl ServerImpersonator for ServerImpersonatorTls { Vec::from(&TLS_INTERNAL_ERROR_ALERT[..]) } - fn dns_resolution_failure_response(&self, _server_name: String) -> Vec { + fn dns_resolution_failure_response(&self, _server_name: Option) -> Vec { Vec::from(&TLS_UNRECOGNIZED_NAME_ALERT[..]) } @@ -75,7 +75,7 @@ mod tests { fn dns_resolution_failure_response_produces_unrecognized_name_alert() { let subject = ServerImpersonatorTls {}; - let result = subject.dns_resolution_failure_response("booga.com".to_string()); + let result = subject.dns_resolution_failure_response(None); assert_eq!(Vec::from(&TLS_UNRECOGNIZED_NAME_ALERT[..]), result); } diff --git a/node/src/proxy_server/tls_protocol_pack.rs b/node/src/proxy_server/tls_protocol_pack.rs index bb81f8543..d167ef0fa 100644 --- a/node/src/proxy_server/tls_protocol_pack.rs +++ b/node/src/proxy_server/tls_protocol_pack.rs @@ -1,13 +1,11 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. -use crate::proxy_server::protocol_pack::{ProtocolPack, ServerImpersonator}; +use crate::proxy_server::protocol_pack::{Host, ProtocolPack, ServerImpersonator}; use crate::proxy_server::server_impersonator_tls::ServerImpersonatorTls; use crate::sub_lib::binary_traverser::BinaryTraverser; use crate::sub_lib::cryptde::PlainData; -use crate::sub_lib::host::Host; use crate::sub_lib::proxy_server::ProxyProtocol; use masq_lib::constants::TLS_PORT; -#[derive(Clone, Copy)] pub struct TlsProtocolPack {} impl ProtocolPack for TlsProtocolPack { @@ -39,21 +37,6 @@ impl ProtocolPack for TlsProtocolPack { fn server_impersonator(&self) -> Box { Box::new(ServerImpersonatorTls {}) } - - fn describe_packet(&self, data: &PlainData) -> String { - match data.get_u8(0) { - Some(0x16u8) => self.describe_handshake(data), - Some(0x14u8) => self.describe_cipher_spec(data), - Some(0x15u8) => self.describe_alert(data), - Some(0x17u8) => self.describe_application_data(data), - Some(opcode) => format!( - "{}-byte packet of unrecognized type 0x{:02X}", - data.len(), - opcode - ), - None => format!("Incomplete {}-byte TLS packet", data.len()), - } - } } impl TlsProtocolPack { @@ -116,89 +99,6 @@ impl TlsProtocolPack { Err(_) => Err(()), } } - - fn describe_handshake(&self, data: &PlainData) -> String { - match data.get_u8(5) { - Some(0x00u8) => "HelloRequest".to_string(), - Some(0x01u8) => self.describe_client_hello(data), - Some(0x02u8) => "ServerHello".to_string(), - Some(0x03u8) => "HelloVerifyRequest".to_string(), - Some(0x04u8) => "NewSessionTicket".to_string(), - Some(0x05u8) => "EndOfEarlyData".to_string(), - Some(0x06u8) => "HelloRetryRequest".to_string(), - Some(0x0Bu8) => "Certificate".to_string(), - Some(0x0Cu8) => "ServerKeyExchange".to_string(), - Some(0x0Du8) => "CertificateRequest".to_string(), - Some(0x0Eu8) => "ServerHelloDone".to_string(), - Some(0x0Fu8) => "CertificateVerify".to_string(), - Some(0x10u8) => "ClientKeyExchange".to_string(), - Some(0x14u8) => "Finished".to_string(), - Some(opcode) => format!("Unrecognized Handshake 0x{:02X}", opcode), - None => format!("Incomplete {}-byte Handshake packet", data.len()), - } - } - - fn describe_client_hello(&self, data: &PlainData) -> String { - match self.find_host(data) { - Some(host) => format!("ClientHello with SNI '{}'", host.name), - None => "ClientHello with no SNI extension".to_string(), - } - } - - fn describe_cipher_spec(&self, data: &PlainData) -> String { - match data.get_u8(5) { - Some(0x01u8) => "ChangeCipherSpec".to_string(), - Some(opcode) => format!("Unrecognized ChangeCipherSpec 0x{:02X}", opcode), - None => format!("Incomplete {}-byte ChangeCipherSpec packet", data.len()), - } - } - - fn describe_alert(&self, data: &PlainData) -> String { - let level = match data.get_u8(5) { - Some(0x01u8) => "Warning".to_string(), - Some(0x02u8) => "Fatal".to_string(), - Some(opcode) => format!("Unrecognized Alert Level 0x{:02X}", opcode), - None => return format!("Incomplete {}-byte Alert packet", data.len()), - }; - let description = match data.get_u8(6) { - Some(0x00) => "CloseNotify".to_string(), - Some(0x01) => "Unrecognized Alert Description 0x01".to_string(), - Some(0x0A) => "UnexpectedMessage".to_string(), - Some(0x14) => "BadRecordMAC".to_string(), - Some(0x15) => "DecryptionFailed".to_string(), - Some(0x16) => "RecordOverflow".to_string(), - Some(0x1E) => "DecompressionFailure".to_string(), - Some(0x28) => "HandshakeFailure".to_string(), - Some(0x29) => "NoCertificate".to_string(), - Some(0x2A) => "BadCertificate".to_string(), - Some(0x2B) => "UnsupportedCertificate".to_string(), - Some(0x2C) => "CertificateRevoked".to_string(), - Some(0x2D) => "CertificateExpired".to_string(), - Some(0x2E) => "CertificateUnknown".to_string(), - Some(0x2F) => "IllegalParameter".to_string(), - Some(0x30) => "UnknownCA".to_string(), - Some(0x31) => "AccessDenied".to_string(), - Some(0x32) => "DecodeError".to_string(), - Some(0x33) => "DecryptError".to_string(), - Some(0x3C) => "ExportRestriction".to_string(), - Some(0x46) => "ProtocolVersion".to_string(), - Some(0x47) => "InsufficientSecurity".to_string(), - Some(0x50) => "InternalError".to_string(), - Some(0x5A) => "UserCanceled".to_string(), - Some(0x64) => "NoRenegotiation".to_string(), - Some(0x72) => "UnsupportedExtension".to_string(), - Some(opcode) => format!("Unrecognized Alert Description 0x{:02X}", opcode), - None => return format!("Incomplete {}-byte Alert packet", data.len()), - }; - format!("{} {}", level, description) - } - - fn describe_application_data(&self, data: &PlainData) -> String { - if data.len() < 5 { - return "Incomplete ApplicationData".to_string(); - } - format!("{}-byte ApplicationData", data.len() - 5) - } } #[cfg(test)] @@ -896,256 +796,4 @@ mod tests { assert_eq!(None, result); } - - #[test] - fn describe_packet_handles_empty_packet() { - let data = PlainData::new(&[]); - - let result = TlsProtocolPack {}.describe_packet(&data); - - assert_eq!("Incomplete 0-byte TLS packet", result); - } - - #[test] - fn describe_packet_handles_unrecognized_packet_type() { - let data = PlainData::new(&[0xFFu8]); - - let result = TlsProtocolPack {}.describe_packet(&data); - - assert_eq!("1-byte packet of unrecognized type 0xFF", result); - } - - #[test] - fn describe_packet_handles_short_handshake_packet() { - let data = PlainData::new(&[0x16u8]); - - let result = TlsProtocolPack {}.describe_packet(&data); - - assert_eq!("Incomplete 1-byte Handshake packet", result); - } - - #[test] - fn identifies_client_hello_with_sni() { - #[rustfmt::skip] - let data = PlainData::new(&[ - 0x16, // content_type: Handshake - 0x00, 0x00, 0x00, 0x00, // version, length: don't care - 0x01, // handshake_type: ClientHello - 0x00, 0x00, 0x00, 0x00, 0x00, // length, version: don't care - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // random: don't care - 0x01, // session_id_length - 0x00, // session_id: don't care - 0x00, 0x01, // cipher_suites_length - 0x00, // cipher_suite: don't care - 0x01, // compression_methods_length - 0x00, // compression_method: don't care - 0x00, 0x13, // extensions_length - 0x00, 0x00, // extension_type: server_name - 0x00, 0x0F, // extension_length - 0x00, 0x0D, // server_name_list_length - 0x00, // server_name_type - 0x00, 0x0A, // server_name_length - b's', b'e', b'r', b'v', b'e', b'r', b'.', b'c', - b'o', b'm', // server_name - ]); - - let result = TlsProtocolPack {}.describe_packet(&data); - - assert_eq!("ClientHello with SNI 'server.com'", result); - } - - #[test] - fn identifies_client_hello_without_sni() { - #[rustfmt::skip] - let data = PlainData::new(&[ - 0x16, // content_type: Handshake - 0x00, 0x00, 0x00, 0x00, // version, length: don't care - 0x01, // handshake_type: ClientHello - 0x00, 0x00, 0x00, 0x00, 0x00, // length, version: don't care - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // random: don't care - 0x00, // session_id_length - 0x00, 0x00, // cipher_suites_length - 0x00, // compression_methods_length - 0x00, 0x00, // extensions_length - ]); - - let result = TlsProtocolPack {}.describe_packet(&data); - - assert_eq!("ClientHello with no SNI extension", result); - } - - #[test] - fn identifies_other_handshakes() { - #[rustfmt::skip] - let mut bytes: Vec = vec![ - 0x16, // content_type: Handshake - 0x00, 0x00, 0x00, 0x00, // version, length: don't care - 0x00, // handshake_type: replace me - ]; - let handshake_types = vec![ - (0x00, "HelloRequest"), - (0x02, "ServerHello"), - (0x03, "HelloVerifyRequest"), - (0x04, "NewSessionTicket"), - (0x05, "EndOfEarlyData"), - (0x06, "HelloRetryRequest"), - (0x07, "Unrecognized Handshake 0x07"), - (0x08, "Unrecognized Handshake 0x08"), - (0x0B, "Certificate"), - (0x0C, "ServerKeyExchange"), - (0x0D, "CertificateRequest"), - (0x0E, "ServerHelloDone"), - (0x0F, "CertificateVerify"), - (0x10, "ClientKeyExchange"), - (0x14, "Finished"), - ]; - handshake_types.iter().for_each(|(opcode, name)| { - bytes[5] = *opcode; - let data = PlainData::new(&bytes); - - let result = TlsProtocolPack {}.describe_packet(&data); - - assert_eq!(*name, result); - }); - } - - #[test] - fn identifies_cipher_spec_packets() { - #[rustfmt::skip] - let mut bytes: Vec = vec![ - 0x14, // content_type: ChangeCipherSpec - 0x00, 0x00, 0x00, 0x00, // version, length: don't care - 0x00, // change_cipher_spec: replace me - ]; - let change_cipher_specs = vec![ - (0x00, "Unrecognized ChangeCipherSpec 0x00"), - (0x01, "ChangeCipherSpec"), - (0x02, "Unrecognized ChangeCipherSpec 0x02"), - ]; - change_cipher_specs.iter().for_each(|(opcode, name)| { - bytes[5] = *opcode; - let data = PlainData::new(&bytes); - - let result = TlsProtocolPack {}.describe_packet(&data); - - assert_eq!(*name, result); - }); - } - - #[test] - fn handles_incomplete_cipher_spec_packet() { - #[rustfmt::skip] - let data = PlainData::new(&[ - 0x14, // content_type: ChangeCipherSpec - ]); - - let result = TlsProtocolPack {}.describe_packet(&data); - - assert_eq!("Incomplete 1-byte ChangeCipherSpec packet", result); - } - - #[test] - fn identifies_alert_packets() { - #[rustfmt::skip] - let mut bytes: Vec = vec![ - 0x15, // content_type: Alert - 0x00, 0x00, 0x00, 0x00, // version, length: don't care - 0x00, // alert_level: replace me - 0x00, // alert_description: replace me - ]; - let alert_levels = vec![ - (0x01, "Warning"), - (0x02, "Fatal"), - (0x03, "Unrecognized Alert Level 0x03"), - (0x04, "Unrecognized Alert Level 0x04"), - ]; - let alert_descriptions = vec![ - (0x00, "CloseNotify"), - (0x01, "Unrecognized Alert Description 0x01"), - (0x0A, "UnexpectedMessage"), - (0x14, "BadRecordMAC"), - (0x15, "DecryptionFailed"), - (0x16, "RecordOverflow"), - (0x1E, "DecompressionFailure"), - (0x28, "HandshakeFailure"), - (0x29, "NoCertificate"), - (0x2A, "BadCertificate"), - (0x2B, "UnsupportedCertificate"), - (0x2C, "CertificateRevoked"), - (0x2D, "CertificateExpired"), - (0x2E, "CertificateUnknown"), - (0x2F, "IllegalParameter"), - (0x30, "UnknownCA"), - (0x31, "AccessDenied"), - (0x32, "DecodeError"), - (0x33, "DecryptError"), - (0x3C, "ExportRestriction"), - (0x46, "ProtocolVersion"), - (0x47, "InsufficientSecurity"), - (0x50, "InternalError"), - (0x5A, "UserCanceled"), - (0x64, "NoRenegotiation"), - (0x72, "UnsupportedExtension"), - (0xFF, "Unrecognized Alert Description 0xFF"), - ]; - alert_descriptions - .iter() - .for_each(|(description, description_name)| { - bytes[6] = *description; - alert_levels.iter().for_each(|(level, level_name)| { - bytes[5] = *level; - let data = PlainData::new(&bytes); - - let result = TlsProtocolPack {}.describe_packet(&data); - - let expected = format!("{} {}", level_name, description_name); - assert_eq!(expected, result); - }); - }); - } - - #[test] - fn handles_incomplete_alert_packet() { - #[rustfmt::skip] - let data = PlainData::new(&[ - 0x15, // content_type: Alert - ]); - - let result = TlsProtocolPack {}.describe_packet(&data); - - assert_eq!("Incomplete 1-byte Alert packet", result); - } - - #[test] - fn identifies_application_data_packets() { - #[rustfmt::skip] - let data = PlainData::new(&[ - 0x17, // content_type: ApplicationData - 0x00, 0x00, 0x00, 0x00, // version, length: don't care - 0x01, 0x02, 0x03, 0x04, 0x05, // data - ]); - - let result = TlsProtocolPack {}.describe_packet(&data); - - assert_eq!("5-byte ApplicationData", result); - } - - #[test] - fn handles_short_application_data_packets() { - #[rustfmt::skip] - let data = PlainData::new(&[ - 0x17, // content_type: ApplicationData - 0x00, 0x00, 0x00, // Too short by one byte - ]); - - let result = TlsProtocolPack {}.describe_packet(&data); - - assert_eq!("Incomplete ApplicationData", result); - } } diff --git a/node/src/stream_handler_pool.rs b/node/src/stream_handler_pool.rs index b759a703c..7aa6d0ff6 100644 --- a/node/src/stream_handler_pool.rs +++ b/node/src/stream_handler_pool.rs @@ -516,7 +516,7 @@ impl StreamHandlerPool { sw_key ); debug!(self.logger, "Masking {} bytes", msg.context.data.len()); - let packet = if msg.context.sequence_number_opt.is_none() { + let packet = if msg.context.sequence_number.is_none() { let masquerader = self.traffic_analyzer.get_masquerader(); match masquerader.mask(msg.context.data.as_slice()) { Ok(masked_data) => SequencedPacket::new(masked_data, 0, false), @@ -849,7 +849,7 @@ mod tests { let peer_addr = SocketAddr::from_str("1.2.3.4:80").unwrap(); let peer_addr_a = peer_addr.clone(); let local_addr = SocketAddr::from_str("1.2.3.5:80").unwrap(); - let reception_port_opt = Some(8081); + let reception_port = Some(8081); let is_clandestine = false; let one_http_req = b"GET http://here.com HTTP/1.1\r\n\r\n".to_vec(); let one_http_req_a = one_http_req.clone(); @@ -901,7 +901,7 @@ mod tests { .add_sub .try_send(AddStreamMsg::new( connection_info, // the stream splitter mock will return mocked reader/writer - reception_port_opt, + reception_port, PortConfiguration::new( vec![Box::new(HttpRequestDiscriminatorFactory::new())], is_clandestine, @@ -922,10 +922,10 @@ mod tests { &dispatcher::InboundClientData { timestamp: dispatcher_record.timestamp, client_addr: peer_addr_a, - reception_port_opt, + reception_port, last_data: false, is_clandestine, - sequence_number_opt: Some(0), + sequence_number: Some(0), data: one_http_req_a, } ); @@ -936,10 +936,10 @@ mod tests { &dispatcher::InboundClientData { timestamp: dispatcher_record.timestamp, client_addr: peer_addr_a, - reception_port_opt, + reception_port, last_data: false, is_clandestine, - sequence_number_opt: Some(1), + sequence_number: Some(1), data: another_http_req_a, } ); @@ -950,10 +950,10 @@ mod tests { &dispatcher::InboundClientData { timestamp: dispatcher_record.timestamp, client_addr: peer_addr_a, - reception_port_opt, + reception_port, last_data: false, is_clandestine, - sequence_number_opt: Some(2), + sequence_number: Some(2), data: a_third_http_req_a, } ); @@ -963,7 +963,7 @@ mod tests { &dispatcher::StreamShutdownMsg { peer_addr: peer_addr_a, stream_type: RemovedStreamType::NonClandestine(NonClandestineAttributes { - reception_port: reception_port_opt.unwrap(), + reception_port: reception_port.unwrap(), sequence_number: 3 }), report_to_counterpart: true, @@ -1025,7 +1025,7 @@ mod tests { .try_send(TransmitDataMsg { endpoint: Endpoint::Socket(peer_addr), last_data: true, - sequence_number_opt: Some(0), + sequence_number: Some(0), data: b"hello".to_vec(), }) .unwrap(); @@ -1107,7 +1107,7 @@ mod tests { .try_send(TransmitDataMsg { endpoint: Endpoint::Socket(peer_addr), last_data: true, - sequence_number_opt: Some(0), + sequence_number: Some(0), data: vec![0x12, 0x34], }) .unwrap(); @@ -1130,7 +1130,7 @@ mod tests { .try_send(TransmitDataMsg { endpoint: Endpoint::Socket(peer_addr), last_data: true, - sequence_number_opt: Some(0), + sequence_number: Some(0), data: vec![0x56, 0x78], }) .unwrap(); @@ -1209,7 +1209,7 @@ mod tests { .try_send(TransmitDataMsg { endpoint: Endpoint::Socket(peer_addr), last_data: true, - sequence_number_opt: Some(0), + sequence_number: Some(0), data: vec![0x12, 0x34], }) .unwrap(); @@ -1381,7 +1381,7 @@ mod tests { context: TransmitDataMsg { endpoint: Endpoint::Key(public_key), last_data: false, - sequence_number_opt: None, + sequence_number: None, data: b"hello".to_vec(), }, }) @@ -1470,7 +1470,7 @@ mod tests { .try_send(TransmitDataMsg { endpoint: Endpoint::Key(public_key.clone()), last_data: false, - sequence_number_opt: None, + sequence_number: None, data: outgoing_unmasked, }) .unwrap(); @@ -1505,10 +1505,10 @@ mod tests { &InboundClientData { timestamp: ibcd.timestamp, client_addr: SocketAddr::from_str("1.2.3.5:7000").unwrap(), - reception_port_opt: Some(54321), + reception_port: Some(54321), last_data: false, is_clandestine: true, - sequence_number_opt: None, + sequence_number: None, data: incoming_unmasked, } ); @@ -1586,7 +1586,7 @@ mod tests { .try_send(TransmitDataMsg { endpoint: Endpoint::Key(key.clone()), last_data: false, - sequence_number_opt: Some(0), + sequence_number: Some(0), data: b"hello".to_vec(), }) .unwrap(); @@ -1646,7 +1646,7 @@ mod tests { context: TransmitDataMsg { endpoint: Endpoint::Key(key.clone()), last_data: false, - sequence_number_opt: Some(0), + sequence_number: Some(0), data: b"hello".to_vec(), }, }) @@ -1698,7 +1698,7 @@ mod tests { context: TransmitDataMsg { endpoint: Endpoint::Key(key.clone()), last_data: false, - sequence_number_opt: None, + sequence_number: None, data: b"hello".to_vec(), }, }) @@ -1728,7 +1728,7 @@ mod tests { let msg = TransmitDataMsg { endpoint: Endpoint::Socket(peer_addr.clone()), last_data: false, - sequence_number_opt: Some(0), + sequence_number: Some(0), data: b"hello".to_vec(), }; let msg_a = msg.clone(); @@ -1840,7 +1840,7 @@ mod tests { context: TransmitDataMsg { endpoint: Endpoint::Socket(peer_addr.clone()), last_data: true, - sequence_number_opt: Some(0), + sequence_number: Some(0), data: b"hello".to_vec(), }, }; @@ -1869,13 +1869,13 @@ mod tests { let msg = TransmitDataMsg { endpoint: Endpoint::Socket(peer_addr.clone()), last_data: false, - sequence_number_opt: None, + sequence_number: None, data: b"hello".to_vec(), }; let msg_a = TransmitDataMsg { endpoint: Endpoint::Socket(peer_addr.clone()), last_data: false, - sequence_number_opt: None, + sequence_number: None, data: b"worlds".to_vec(), }; let expected_data = JsonMasquerader::new().mask(&msg_a.data).unwrap(); @@ -1995,7 +1995,7 @@ mod tests { context: TransmitDataMsg { endpoint: Endpoint::Socket(peer_addr.clone()), last_data: true, - sequence_number_opt: Some(0), + sequence_number: Some(0), data: b"hello".to_vec(), }, }); @@ -2035,7 +2035,7 @@ mod tests { let msg = TransmitDataMsg { endpoint: Endpoint::Socket(peer_addr.clone()), last_data: false, - sequence_number_opt: None, + sequence_number: None, data: b"hello".to_vec(), }; @@ -2126,7 +2126,7 @@ mod tests { .try_send(TransmitDataMsg { endpoint: Endpoint::Socket(peer_addr), last_data: false, - sequence_number_opt: None, + sequence_number: None, data: hello, }) .unwrap(); @@ -2136,7 +2136,7 @@ mod tests { .try_send(TransmitDataMsg { endpoint: Endpoint::Socket(peer_addr), last_data: false, - sequence_number_opt: None, + sequence_number: None, data: worlds, }) .unwrap(); @@ -2200,7 +2200,7 @@ mod tests { .try_send(TransmitDataMsg { endpoint: Endpoint::Socket(peer_addr), last_data: false, - sequence_number_opt: None, + sequence_number: None, data: b"hello".to_vec(), }) .unwrap(); @@ -2249,7 +2249,7 @@ mod tests { .try_send(TransmitDataMsg { endpoint: Endpoint::Socket(local_addr), last_data: false, - sequence_number_opt: Some(0), + sequence_number: Some(0), data: outgoing_unmasked, }) .unwrap(); diff --git a/node/src/stream_reader.rs b/node/src/stream_reader.rs index df8dd8f33..cac9211a6 100644 --- a/node/src/stream_reader.rs +++ b/node/src/stream_reader.rs @@ -19,7 +19,7 @@ pub struct StreamReaderReal { stream: Box, local_addr: SocketAddr, peer_addr: SocketAddr, - reception_port_opt: Option, + reception_port: Option, ibcd_sub: Recipient, remove_sub: Recipient, dispatcher_stream_shutdown_sub: Recipient, @@ -86,7 +86,7 @@ impl StreamReaderReal { #[allow(clippy::too_many_arguments)] pub fn new( stream: Box, - reception_port_opt: Option, + reception_port: Option, ibcd_sub: Recipient, remove_sub: Recipient, dispatcher_sub: Recipient, @@ -107,7 +107,7 @@ impl StreamReaderReal { stream, local_addr, peer_addr, - reception_port_opt, + reception_port, ibcd_sub, remove_sub, dispatcher_stream_shutdown_sub: dispatcher_sub, @@ -137,7 +137,7 @@ impl StreamReaderReal { // handshake and should start the sequence at Some(0) as well, the ProxyServer will // handle the sequenced packet offset before sending them through the stream_writer // and avoid dropping duplicate packets. - let sequence_number_opt = if unmasked_chunk.sequenced && !is_connect { + let sequence_number = if unmasked_chunk.sequenced && !is_connect { Some(self.sequencer.next_sequence_number()) } else if is_connect { // This case needs to explicitly be Some(0) instead of None so that the StreamHandlerPool does @@ -146,7 +146,7 @@ impl StreamReaderReal { } else { None }; - match sequence_number_opt { + match sequence_number { Some(num) => debug!( self.logger, "Read {} bytes of clear data (#{})", @@ -162,10 +162,10 @@ impl StreamReaderReal { let msg = dispatcher::InboundClientData { timestamp: SystemTime::now(), client_addr: self.peer_addr, - reception_port_opt: self.reception_port_opt, + reception_port: self.reception_port, last_data: false, is_clandestine: self.is_clandestine, - sequence_number_opt, + sequence_number, data: unmasked_chunk.chunk.clone(), }; debug!(self.logger, "Discriminator framed and unmasked {} bytes for {}; transmitting via Hopper", @@ -181,7 +181,7 @@ impl StreamReaderReal { } fn shutdown(&mut self) { - debug!(self.logger, "Directing removal of {}clandestine StreamReader with reception_port {:?} on {} listening to {}", if self.is_clandestine {""} else {"non-"}, self.reception_port_opt, self.local_addr, self.peer_addr); + debug!(self.logger, "Directing removal of {}clandestine StreamReader with reception_port {:?} on {} listening to {}", if self.is_clandestine {""} else {"non-"}, self.reception_port, self.local_addr, self.peer_addr); self.remove_sub .try_send(RemoveStreamMsg { peer_addr: self.peer_addr, @@ -190,7 +190,7 @@ impl StreamReaderReal { RemovedStreamType::Clandestine } else { RemovedStreamType::NonClandestine(NonClandestineAttributes { - reception_port: self.reception_port_opt.expect( + reception_port: self.reception_port.expect( "Non-clandestine StreamReader should always have a reception_port", ), sequence_number: self.sequencer.next_sequence_number(), @@ -511,10 +511,10 @@ mod tests { &dispatcher::InboundClientData { timestamp: d_record.timestamp, client_addr: peer_addr, - reception_port_opt: Some(1234 as u16), + reception_port: Some(1234 as u16), last_data: false, is_clandestine: true, - sequence_number_opt: Some(0), + sequence_number: Some(0), data: Vec::from("GET http://here.com HTTP/1.1\r\n\r\n".as_bytes()), } ); @@ -575,13 +575,13 @@ mod tests { Some(0), d_recording .get_record::(0) - .sequence_number_opt, + .sequence_number, ); assert_eq!( Some(0), d_recording .get_record::(1) - .sequence_number_opt, + .sequence_number, ); } @@ -633,10 +633,10 @@ mod tests { &dispatcher::InboundClientData { timestamp: d_record.timestamp, client_addr: peer_addr, - reception_port_opt: Some(1234 as u16), + reception_port: Some(1234 as u16), last_data: false, is_clandestine: false, - sequence_number_opt: Some(0), + sequence_number: Some(0), data: Vec::from("GET http://here.com HTTP/1.1\r\n\r\n".as_bytes()), } ); @@ -648,10 +648,10 @@ mod tests { &dispatcher::InboundClientData { timestamp: d_record.timestamp, client_addr: peer_addr, - reception_port_opt: Some(1234 as u16), + reception_port: Some(1234 as u16), last_data: false, is_clandestine: false, - sequence_number_opt: Some(1), + sequence_number: Some(1), data: Vec::from("GET http://www.example.com HTTP/1.1\r\n\r\n".as_bytes()), } ); @@ -707,10 +707,10 @@ mod tests { &dispatcher::InboundClientData { timestamp: d_record.timestamp, client_addr, - reception_port_opt: Some(1234 as u16), + reception_port: Some(1234 as u16), last_data: false, is_clandestine: true, - sequence_number_opt: None, + sequence_number: None, data: Vec::from("GET http://here.com HTTP/1.1\r\n\r\n".as_bytes()), } ); diff --git a/node/src/sub_lib/dispatcher.rs b/node/src/sub_lib/dispatcher.rs index e04401220..66e349e11 100644 --- a/node/src/sub_lib/dispatcher.rs +++ b/node/src/sub_lib/dispatcher.rs @@ -116,10 +116,10 @@ pub enum DispatcherError { pub struct InboundClientData { pub timestamp: SystemTime, pub client_addr: SocketAddr, - pub reception_port_opt: Option, + pub reception_port: Option, pub last_data: bool, pub is_clandestine: bool, - pub sequence_number_opt: Option, + pub sequence_number: Option, pub data: Vec, } @@ -130,7 +130,7 @@ impl Debug for InboundClientData { Err(_) => self.data.hex_dump().to_string(), }; write!(f, "InboundClientData {{ peer_addr: {:?}, reception_port: {:?}, last_data: {}, sequence_number: {:?}, {} bytes of data: {} }}", - self.client_addr, self.reception_port_opt, self.last_data, self.sequence_number_opt, self.data.len(), data_string) + self.client_addr, self.reception_port, self.last_data, self.sequence_number, self.data.len(), data_string) } } @@ -139,10 +139,10 @@ impl InboundClientData { InboundClientData { timestamp: SystemTime::now(), client_addr: self.client_addr, - reception_port_opt: self.reception_port_opt, + reception_port: self.reception_port, last_data: self.last_data, is_clandestine: self.is_clandestine, - sequence_number_opt: self.sequence_number_opt, + sequence_number: self.sequence_number, data: vec![], } } @@ -274,10 +274,10 @@ mod tests { let subject = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.4.3.2:9999").unwrap(), - reception_port_opt: None, + reception_port: None, last_data: false, is_clandestine: false, - sequence_number_opt: None, + sequence_number: None, data: b"CONNECT server.example.com:80 HTTP/1.1\r\nHost: server.example.com:80\r\nProxy-Authorization: basic aGVsbG86d29ybGQ=\r\n\r\n".to_vec(), }; @@ -289,10 +289,10 @@ mod tests { let subject = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.4.3.2:9999").unwrap(), - reception_port_opt: None, + reception_port: None, last_data: false, is_clandestine: false, - sequence_number_opt: None, + sequence_number: None, data: b"GET server.example.com:80 HTTP/1.1\r\nHost: server.example.com:80\r\nProxy-Authorization: basic aGVsbG86d29ybGQ=\r\n\r\n".to_vec(), }; @@ -304,10 +304,10 @@ mod tests { let subject = InboundClientData { timestamp: SystemTime::now(), client_addr: SocketAddr::from_str("1.4.3.2:9999").unwrap(), - reception_port_opt: None, + reception_port: None, last_data: false, is_clandestine: false, - sequence_number_opt: None, + sequence_number: None, data: b"CONNECTX".to_vec(), }; diff --git a/node/src/sub_lib/hopper.rs b/node/src/sub_lib/hopper.rs index 08af13bb5..96d756ef2 100644 --- a/node/src/sub_lib/hopper.rs +++ b/node/src/sub_lib/hopper.rs @@ -173,7 +173,6 @@ mod tests { use crate::sub_lib::cryptde::PlainData; use crate::sub_lib::dispatcher::Component; use crate::sub_lib::route::RouteSegment; - use crate::sub_lib::stream_key::StreamKey; use crate::test_utils::recorder::Recorder; use crate::test_utils::{make_meaningless_message_type, make_paying_wallet}; use actix::Actor; @@ -206,7 +205,7 @@ mod tests { let cryptde = CRYPTDE_PAIR.main.as_ref(); let public_key = PublicKey::new(&[1, 2]); let node_addr = NodeAddr::new(&IpAddr::from_str("1.2.3.4").unwrap(), &[1, 2, 3, 4]); - let payload = make_meaningless_message_type(StreamKey::make_meaningless_stream_key()); + let payload = make_meaningless_message_type(); let result = NoLookupIncipientCoresPackage::new(cryptde, &public_key, &node_addr, payload.clone()); @@ -232,7 +231,7 @@ mod tests { cryptde, &PublicKey::new(&[]), &NodeAddr::new(&IpAddr::from_str("1.1.1.1").unwrap(), &[]), - make_meaningless_message_type(StreamKey::make_meaningless_stream_key()), + make_meaningless_message_type(), ); assert_eq!( result, @@ -256,7 +255,7 @@ mod tests { Some(TEST_DEFAULT_CHAIN.rec().contract), ) .unwrap(); - let payload = make_meaningless_message_type(StreamKey::make_meaningless_stream_key()); + let payload = make_meaningless_message_type(); let result = IncipientCoresPackage::new(cryptde, route.clone(), payload.clone(), &key56); let subject = result.unwrap(); @@ -279,7 +278,7 @@ mod tests { let result = IncipientCoresPackage::new( cryptde, Route { hops: vec![] }, - make_meaningless_message_type(StreamKey::make_meaningless_stream_key()), + make_meaningless_message_type(), &PublicKey::new(&[]), ); @@ -305,7 +304,7 @@ mod tests { Some(TEST_DEFAULT_CHAIN.rec().contract), ) .unwrap(); - let payload = make_meaningless_message_type(StreamKey::make_meaningless_stream_key()); + let payload = make_meaningless_message_type(); let subject: ExpiredCoresPackage = ExpiredCoresPackage::new( immediate_neighbor, diff --git a/node/src/sub_lib/host.rs b/node/src/sub_lib/host.rs deleted file mode 100644 index bb23d8d16..000000000 --- a/node/src/sub_lib/host.rs +++ /dev/null @@ -1,39 +0,0 @@ -use std::fmt::Display; - -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Host { - pub name: String, - pub port: u16, -} - -impl Display for Host { - fn fmt(&self, f: &mut std::fmt::Formatter) -> Result<(), std::fmt::Error> { - write!(f, "{}:{}", self.name, self.port) - } -} - -impl Host { - pub fn new(name: &str, port: u16) -> Host { - Host { - name: name.to_string(), - port, - } - } -} - -#[cfg(test)] -mod tests { - use super::*; - - #[test] - fn display() { - let subject = Host { - name: "example.com".to_string(), - port: 8080, - }; - - let result = format!("{}", subject); - - assert_eq!(result, "example.com:8080".to_string()); - } -} diff --git a/node/src/sub_lib/http_packet_framer.rs b/node/src/sub_lib/http_packet_framer.rs index 28d4f895f..29685f46b 100644 --- a/node/src/sub_lib/http_packet_framer.rs +++ b/node/src/sub_lib/http_packet_framer.rs @@ -104,7 +104,7 @@ impl HttpPacketFramer { lines: Vec::new(), }, start_finder, - logger: Logger::new("HttpPacketFramer"), + logger: Logger::new("HttpRequestFramer"), } } diff --git a/node/src/sub_lib/migrations/client_request_payload.rs b/node/src/sub_lib/migrations/client_request_payload.rs index b048c1900..2bd9993f5 100644 --- a/node/src/sub_lib/migrations/client_request_payload.rs +++ b/node/src/sub_lib/migrations/client_request_payload.rs @@ -49,7 +49,7 @@ impl TryFrom<&Value> for ClientRequestPayload_0v1 { Value::Map(map) => { let mut stream_key_opt: Option = None; let mut sequenced_packet_opt: Option = None; - let mut target_hostname: Option = None; + let mut target_hostname_opt: Option> = None; let mut target_port_opt: Option = None; let mut protocol_opt: Option = None; let mut originator_public_key_opt: Option = None; @@ -61,7 +61,9 @@ impl TryFrom<&Value> for ClientRequestPayload_0v1 { "sequenced_packet" => { sequenced_packet_opt = value_to_type::(v) } - "target_hostname" => target_hostname = value_to_type::(v), + "target_hostname" => { + target_hostname_opt = value_to_type::>(v) + } "target_port" => target_port_opt = value_to_type::(v), "protocol" => protocol_opt = value_to_type::(v), "originator_public_key" => { @@ -87,7 +89,7 @@ impl TryFrom<&Value> for ClientRequestPayload_0v1 { "sequenced_packet", &sequenced_packet_opt, ); - check_field(&mut missing_fields, "target_hostname", &target_hostname); + check_field(&mut missing_fields, "target_hostname", &target_hostname_opt); check_field(&mut missing_fields, "target_port", &target_port_opt); check_field(&mut missing_fields, "protocol", &protocol_opt); check_field( @@ -101,7 +103,7 @@ impl TryFrom<&Value> for ClientRequestPayload_0v1 { Ok(ClientRequestPayload_0v1 { stream_key: stream_key_opt.expect("stream_key disappeared"), sequenced_packet: sequenced_packet_opt.expect("sequenced_packet disappeared"), - target_hostname: target_hostname.expect("target_hostname disappeared"), + target_hostname: target_hostname_opt.expect("target_hostname disappeared"), target_port: target_port_opt.expect("target_port disappeared"), protocol: protocol_opt.expect("protocol disappeared"), originator_public_key: originator_public_key_opt @@ -129,7 +131,7 @@ mod tests { struct ExampleFutureCRP { pub stream_key: StreamKey, pub sequenced_packet: SequencedPacket, - pub target_hostname: String, + pub target_hostname: Option, pub target_port: u16, pub protocol: ProxyProtocol, pub originator_public_key: PublicKey, @@ -139,7 +141,7 @@ mod tests { let expected_crp = ClientRequestPayload_0v1 { stream_key: StreamKey::make_meaningful_stream_key("All Things Must Pass"), sequenced_packet: SequencedPacket::new(vec![4, 3, 2, 1], 4321, false), - target_hostname: "target.hostname.com".to_string(), + target_hostname: Some("target.hostname.com".to_string()), target_port: 1234, protocol: ProxyProtocol::HTTP, originator_public_key: PublicKey::new(&[2, 3, 4, 5]), diff --git a/node/src/sub_lib/mod.rs b/node/src/sub_lib/mod.rs index 4c1a76fce..51360357b 100644 --- a/node/src/sub_lib/mod.rs +++ b/node/src/sub_lib/mod.rs @@ -21,7 +21,6 @@ pub mod framer; pub mod framer_utils; pub mod hop; pub mod hopper; -pub mod host; pub mod http_packet_framer; pub mod http_response_start_finder; pub mod limiter; diff --git a/node/src/sub_lib/neighborhood.rs b/node/src/sub_lib/neighborhood.rs index 674f83191..f26282aa6 100644 --- a/node/src/sub_lib/neighborhood.rs +++ b/node/src/sub_lib/neighborhood.rs @@ -8,7 +8,6 @@ use crate::sub_lib::cryptde::{CryptDE, PublicKey}; use crate::sub_lib::cryptde_real::CryptDEReal; use crate::sub_lib::dispatcher::{Component, StreamShutdownMsg}; use crate::sub_lib::hopper::ExpiredCoresPackage; -use crate::sub_lib::host::Host; use crate::sub_lib::node_addr::NodeAddr; use crate::sub_lib::peer_actors::{BindMessage, NewPublicIp, StartMessage}; use crate::sub_lib::route::Route; @@ -474,7 +473,7 @@ pub struct RouteQueryMessage { pub target_component: Component, pub return_component_opt: Option, pub payload_size: usize, - pub host: Host, + pub hostname_opt: Option, } impl Message for RouteQueryMessage { @@ -482,13 +481,16 @@ impl Message for RouteQueryMessage { } impl RouteQueryMessage { - pub fn data_indefinite_route_request(host: Host, payload_size: usize) -> RouteQueryMessage { + pub fn data_indefinite_route_request( + hostname_opt: Option, + payload_size: usize, + ) -> RouteQueryMessage { RouteQueryMessage { target_key_opt: None, target_component: Component::ProxyClient, return_component_opt: Some(Component::ProxyServer), payload_size, - host, + hostname_opt, } } } @@ -500,53 +502,16 @@ pub enum ExpectedService { Nothing, } -impl ExpectedService { - pub fn exit_node_key_opt(&self) -> Option { - match self { - ExpectedService::Exit(key, _, _) => Some(key.clone()), - _ => None, - } - } - - pub fn public_key_opt(&self) -> Option { - match self { - ExpectedService::Exit(key, _, _) | ExpectedService::Routing(key, _, _) => { - Some(key.clone()) - } - _ => None, - } - } - - pub fn wallet_opt(&self) -> Option<&Wallet> { - match self { - ExpectedService::Exit(_, wallet, _) | ExpectedService::Routing(_, wallet, _) => { - Some(wallet) - } - _ => None, - } - } - - pub fn rate_pack_opt(&self) -> Option<&RatePack> { - match self { - ExpectedService::Exit(_, _, rate_pack) | ExpectedService::Routing(_, _, rate_pack) => { - Some(rate_pack) - } - _ => None, - } - } -} - #[derive(Clone, Debug, PartialEq, Eq)] pub enum ExpectedServices { OneWay(Vec), - RoundTrip(Vec, Vec), + RoundTrip(Vec, Vec, u32), } #[derive(Clone, Debug, PartialEq, Eq)] pub struct RouteQueryResponse { pub route: Route, pub expected_services: ExpectedServices, - pub host: Host, } #[derive(Clone, Debug, Message, PartialEq, Eq)] @@ -1095,8 +1060,7 @@ mod tests { #[test] fn data_indefinite_route_request() { - let result = - RouteQueryMessage::data_indefinite_route_request(Host::new("booga.com", 1234), 7500); + let result = RouteQueryMessage::data_indefinite_route_request(None, 7500); assert_eq!( result, @@ -1105,7 +1069,7 @@ mod tests { target_component: Component::ProxyClient, return_component_opt: Some(Component::ProxyServer), payload_size: 7500, - host: Host::new("booga.com", 1234), + hostname_opt: None, } ); } diff --git a/node/src/sub_lib/proxy_server.rs b/node/src/sub_lib/proxy_server.rs index c7b0ab49c..c3042859f 100644 --- a/node/src/sub_lib/proxy_server.rs +++ b/node/src/sub_lib/proxy_server.rs @@ -4,7 +4,7 @@ use crate::sub_lib::data_version::DataVersion; use crate::sub_lib::dispatcher::InboundClientData; use crate::sub_lib::dispatcher::StreamShutdownMsg; use crate::sub_lib::hopper::{ExpiredCoresPackage, MessageType}; -use crate::sub_lib::neighborhood::RouteQueryResponse; +use crate::sub_lib::neighborhood::{ExpectedService, RouteQueryResponse}; use crate::sub_lib::peer_actors::BindMessage; use crate::sub_lib::proxy_client::{ClientResponsePayload_0v1, DnsResolveFailure_0v1}; use crate::sub_lib::sequence_buffer::SequencedPacket; @@ -34,7 +34,7 @@ pub enum ProxyProtocol { pub struct ClientRequestPayload_0v1 { pub stream_key: StreamKey, pub sequenced_packet: SequencedPacket, - pub target_hostname: String, + pub target_hostname: Option, pub target_port: u16, pub protocol: ProxyProtocol, pub originator_public_key: PublicKey, @@ -55,6 +55,14 @@ impl ClientRequestPayload_0v1 { } } +#[derive(Message, Debug, PartialEq, Eq)] +pub struct AddReturnRouteMessage { + pub return_route_id: u32, + pub expected_services: Vec, + pub protocol: ProxyProtocol, + pub hostname_opt: Option, +} + #[derive(Message, Debug, PartialEq, Eq)] pub struct AddRouteResultMessage { pub stream_key: StreamKey, @@ -73,6 +81,7 @@ pub struct ProxyServerSubs { pub from_dispatcher: Recipient, pub from_hopper: Recipient>, pub dns_failure_from_hopper: Recipient>, + pub add_return_route: Recipient, pub stream_shutdown_sub: Recipient, pub node_from_ui: Recipient, pub route_result_sub: Recipient, @@ -104,6 +113,7 @@ mod tests { recorder, ExpiredCoresPackage ), + add_return_route: recipient!(recorder, AddReturnRouteMessage), stream_shutdown_sub: recipient!(recorder, StreamShutdownMsg), node_from_ui: recipient!(recorder, NodeFromUiMessage), route_result_sub: recipient!(recorder, AddRouteResultMessage), diff --git a/node/src/sub_lib/route.rs b/node/src/sub_lib/route.rs index 05bcc15ad..6d9b52604 100644 --- a/node/src/sub_lib/route.rs +++ b/node/src/sub_lib/route.rs @@ -1,4 +1,5 @@ // Copyright (c) 2019, MASQ (https://masq.ai) and/or its affiliates. All rights reserved. +use crate::sub_lib::cryptde::encodex; use crate::sub_lib::cryptde::CryptDE; use crate::sub_lib::cryptde::CryptData; use crate::sub_lib::cryptde::PublicKey; @@ -32,6 +33,7 @@ impl Route { cryptde, None, None, + None, ) } @@ -46,6 +48,7 @@ impl Route { None, cryptde, consuming_wallet, + None, contract_address, ) } @@ -53,8 +56,9 @@ impl Route { pub fn round_trip( route_segment_over: RouteSegment, route_segment_back: RouteSegment, - cryptde: &dyn CryptDE, // Doesn't matter which CryptDE: only used for encoding. + cryptde: &dyn CryptDE, // Must be the CryptDE of the originating Node: used to encrypt return_route_id. consuming_wallet: Option, + return_route_id: u32, contract_address: Option
, ) -> Result { Self::construct( @@ -62,10 +66,22 @@ impl Route { Some(route_segment_back), cryptde, consuming_wallet, + Some(return_route_id), contract_address, ) } + pub fn id(&self, cryptde: &dyn CryptDE) -> Result { + if let Some(first) = self.hops.first() { + match decodex(cryptde, first) { + Ok(n) => Ok(n), + Err(e) => Err(format!("{:?}", e)), + } + } else { + Err("Response route did not contain a return route ID".to_string()) + } + } + // This cryptde must be the CryptDE of the next hop to come off the Route. pub fn next_hop(&self, cryptde: &dyn CryptDE) -> Result { match self.hops.first() { @@ -116,7 +132,15 @@ impl Route { last_cryptde.public_key(), live_hop ), - Err(error) => format!("{}\nError: {:?}", most_strings, error), + Err(outside) => match decodex::(last_cryptde, &last_hop_enc) { + Ok(return_route_id) => format!( + "{}\nEncrypted with {:?}: Return Route ID: {}\n", + most_strings, + last_cryptde.public_key(), + return_route_id + ), + Err(inside) => format!("{}\nError: {:?} / {:?}", most_strings, outside, inside), + }, } } @@ -125,6 +149,7 @@ impl Route { back: Option, cryptde: &dyn CryptDE, consuming_wallet: Option, + return_route_id_opt: Option, contract_address: Option
, ) -> Result { if let Some(error) = Route::validate_route_segments(&over, &back) { @@ -149,7 +174,12 @@ impl Route { contract_address, ); - Route::hops_to_route(hops[0..].to_vec(), &over.keys[0], cryptde) + Route::hops_to_route( + hops[0..].to_vec(), + &over.keys[0], + return_route_id_opt, + cryptde, + ) } fn over_segment<'a>( @@ -266,6 +296,7 @@ impl Route { fn hops_to_route( hops: Vec, top_hop_key: &PublicKey, + return_route_id_opt: Option, cryptde: &dyn CryptDE, ) -> Result { let mut hops_enc: Vec = Vec::new(); @@ -277,8 +308,17 @@ impl Route { }); hop_key = &data_hop.public_key; } + if let Some(return_route_id) = return_route_id_opt { + let return_route_id_enc = Self::encrypt_return_route_id(return_route_id, cryptde); + hops_enc.push(return_route_id_enc); + } Ok(Route { hops: hops_enc }) } + + fn encrypt_return_route_id(return_route_id: u32, cryptde: &dyn CryptDE) -> CryptData { + encodex(cryptde, cryptde.public_key(), &return_route_id) + .expect("Internal error encrypting u32 return_route_id") + } } pub struct RouteSegment { @@ -325,6 +365,66 @@ mod tests { static ref CRYPTDE_PAIR: CryptDEPair = CryptDEPair::null(); } + #[test] + fn id_decodes_return_route_id() { + let cryptde = CRYPTDE_PAIR.main.as_ref(); + + let subject = Route { + hops: vec![Route::encrypt_return_route_id(42, cryptde)], + }; + + assert_eq!(subject.id(cryptde), Ok(42)); + } + + #[test] + fn id_returns_empty_route_error_when_the_route_is_empty() { + let cryptde = CRYPTDE_PAIR.main.as_ref(); + + let subject = Route { hops: vec![] }; + + assert_eq!( + subject.id(cryptde), + Err("Response route did not contain a return route ID".to_string()) + ); + } + + #[test] + #[should_panic(expected = "Could not decrypt with ebe5f9a0e2 data beginning with ebe5f9a0e1")] + fn id_returns_error_when_the_id_fails_to_decrypt() { + let cryptde1 = CryptDENull::from(&PublicKey::new(b"key a"), TEST_DEFAULT_CHAIN); + let cryptde2 = CryptDENull::from(&PublicKey::new(b"key b"), TEST_DEFAULT_CHAIN); + let subject = Route { + hops: vec![Route::encrypt_return_route_id(42, &cryptde1)], + }; + + let _ = subject.id(&cryptde2); + } + + #[test] + fn route_segments_are_represented_in_base64_by_debug() { + let public_key_data_1: Vec = vec![12, 34, 56, 78, 90]; + let public_key_data_2: Vec = vec![34, 56, 78, 90, 12]; + let public_key_data_3: Vec = vec![56, 78, 90, 12, 34]; + let subject = RouteSegment::new( + vec![ + &PublicKey::new(public_key_data_1.as_slice()), + &PublicKey::new(public_key_data_2.as_slice()), + &PublicKey::new(public_key_data_3.as_slice()), + ], + Component::ProxyClient, + ); + + let result = format!("{:?}", subject); + + let base64_1 = base64::encode_config(&public_key_data_1, base64::STANDARD_NO_PAD); + let base64_2 = base64::encode_config(&public_key_data_2, base64::STANDARD_NO_PAD); + let base64_3 = base64::encode_config(&public_key_data_3, base64::STANDARD_NO_PAD); + assert_eq!( + result, + format!("{} -> {} -> {} : ProxyClient", base64_1, base64_2, base64_3) + ); + } + #[test] fn construct_does_not_like_route_segments_with_too_few_keys() { let cryptde = CRYPTDE_PAIR.main.as_ref(); @@ -358,6 +458,7 @@ mod tests { RouteSegment::new(vec![&c_key, &d_key], Component::ProxyServer), cryptde, Some(paying_wallet.clone()), + 0, Some(TEST_DEFAULT_CHAIN.rec().contract), ) .err() @@ -401,6 +502,7 @@ mod tests { let f_key = PublicKey::new(&[70, 70, 70]); let cryptde = CRYPTDE_PAIR.main.as_ref(); let paying_wallet = make_paying_wallet(b"wallet"); + let return_route_id = 4321; let contract_address = TEST_DEFAULT_CHAIN.rec().contract; let subject = Route::round_trip( @@ -408,6 +510,7 @@ mod tests { RouteSegment::new(vec![&d_key, &e_key, &f_key, &a_key], Component::ProxyServer), cryptde, Some(paying_wallet.clone()), + return_route_id, Some(contract_address.clone()), ) .unwrap(); @@ -496,6 +599,12 @@ mod tests { .unwrap(), "seventh hop" ); + + assert_eq!( + subject.hops[7], + Route::encrypt_return_route_id(return_route_id, cryptde), + "eighth hop" + ); } #[test] @@ -674,6 +783,7 @@ mod tests { RouteSegment::new(vec![&key2, &key1], Component::ProxyServer), cryptde, Some(paying_wallet), + 1234, Some(TEST_DEFAULT_CHAIN.rec().contract), ) .unwrap(); @@ -722,13 +832,13 @@ Encrypted with 0x03040506: LiveHop { public_key: 0x, payer: Some(Payer { wallet: let key1 = PublicKey::new(&[1, 2, 3, 4]); let key2 = PublicKey::new(&[2, 3, 4, 5]); let key3 = PublicKey::new(&[3, 4, 5, 6]); - let cryptde = CryptDENull::from(&key1, TEST_DEFAULT_CHAIN); let paying_wallet = make_paying_wallet(b"wallet"); let subject = Route::round_trip( RouteSegment::new(vec![&key1, &key2, &key3], Component::ProxyClient), RouteSegment::new(vec![&key3, &key2, &key1], Component::ProxyServer), - &cryptde, + &CryptDENull::from(&key1, TEST_DEFAULT_CHAIN), Some(paying_wallet), + 1234, Some(TEST_DEFAULT_CHAIN.rec().contract), ) .unwrap(); @@ -751,6 +861,7 @@ Encrypted with 0x02030405: LiveHop { public_key: 0x03040506, payer: Some(Payer { Encrypted with 0x03040506: LiveHop { public_key: 0x02030405, payer: Some(Payer { wallet: Wallet { kind: Address(0x71d0fc7d1c570b1ed786382b551a09391c91e33d) }, proof: Signature { v: 1, r: "8649b8f6db6232cb1e4f1f04786ad4ef33488c968e64bec74ecd893d6d05c1b9", s: "8649b8f6db6232cb1e4f1f04786ad4ef33488c968e64bec74ecd893d6d05c1b9" } }), component: ProxyClient } Encrypted with 0x02030405: LiveHop { public_key: 0x01020304, payer: Some(Payer { wallet: Wallet { kind: Address(0x71d0fc7d1c570b1ed786382b551a09391c91e33d) }, proof: Signature { v: 0, r: "4324a40295bb36ef2b927fb24250fe42397a57b861ea152bbbe4f84150d4ff5a", s: "4324a40295bb36ef2b927fb24250fe42397a57b861ea152bbbe4f84150d4ff5a" } }), component: Hopper } Encrypted with 0x01020304: LiveHop { public_key: 0x, payer: Some(Payer { wallet: Wallet { kind: Address(0x71d0fc7d1c570b1ed786382b551a09391c91e33d) }, proof: Signature { v: 0, r: "3e3a92d7284c2c2ff7119e9f7a7e183b062a335a598e965a47c36a2f288b6f8d", s: "3e3a92d7284c2c2ff7119e9f7a7e183b062a335a598e965a47c36a2f288b6f8d" } }), component: ProxyServer } +Encrypted with 0x01020304: Return Route ID: 1234 "# ) ); diff --git a/node/src/sub_lib/sequence_buffer.rs b/node/src/sub_lib/sequence_buffer.rs index 2cb101c41..c73dbc021 100644 --- a/node/src/sub_lib/sequence_buffer.rs +++ b/node/src/sub_lib/sequence_buffer.rs @@ -38,7 +38,7 @@ impl<'a> From<&'a TransmitDataMsg> for SequencedPacket { fn from(tdm: &'a TransmitDataMsg) -> Self { SequencedPacket::new( tdm.data.clone(), - tdm.sequence_number_opt.unwrap_or(0), + tdm.sequence_number.unwrap_or(0), tdm.last_data, ) } @@ -253,7 +253,7 @@ mod tests { endpoint: Endpoint::Socket(SocketAddr::from_str("1.2.3.4:80").unwrap()), last_data: true, data: vec![1, 4, 5, 9], - sequence_number_opt: None, + sequence_number: None, }; let result = SequencedPacket::from(&tdm); @@ -267,7 +267,7 @@ mod tests { endpoint: Endpoint::Socket(SocketAddr::from_str("1.2.3.4:80").unwrap()), last_data: true, data: vec![1, 4, 5, 9], - sequence_number_opt: Some(1), + sequence_number: Some(1), }; let result = SequencedPacket::from(&tdm); @@ -280,7 +280,7 @@ mod tests { endpoint: Endpoint::Socket(SocketAddr::from_str("1.2.3.4:80").unwrap()), last_data: false, data: vec![4, 2, 5, 67], - sequence_number_opt: Some(4), + sequence_number: Some(4), }; let result = SequencedPacket::from(&tdm); diff --git a/node/src/sub_lib/stream_handler_pool.rs b/node/src/sub_lib/stream_handler_pool.rs index 92ade9931..fcb0f2e64 100644 --- a/node/src/sub_lib/stream_handler_pool.rs +++ b/node/src/sub_lib/stream_handler_pool.rs @@ -8,7 +8,7 @@ use actix::Message; pub struct TransmitDataMsg { pub endpoint: Endpoint, pub last_data: bool, - pub sequence_number_opt: Option, // Some implies clear data; None implies clandestine. + pub sequence_number: Option, // Some implies clear data; None implies clandestine. pub data: Vec, } diff --git a/node/src/sub_lib/stream_key.rs b/node/src/sub_lib/stream_key.rs index 8e6629ab5..bca70efe4 100644 --- a/node/src/sub_lib/stream_key.rs +++ b/node/src/sub_lib/stream_key.rs @@ -93,22 +93,13 @@ impl StreamKey { hash: hash.digest().bytes(), } } - - #[cfg(test)] - pub fn from_bytes(bytes: &[u8]) -> StreamKey { - let mut hash = [0xA; sha1::DIGEST_LENGTH]; - for i in 0..std::cmp::min(sha1::DIGEST_LENGTH, bytes.len()) { - hash[i] = bytes[i]; - } - StreamKey { hash } - } } impl StreamKey { pub fn make_meaningless_stream_key() -> StreamKey { - let mut bytes = [0; sha1::DIGEST_LENGTH]; - randombytes_into(&mut bytes); - StreamKey { hash: bytes } + StreamKey { + hash: [0; sha1::DIGEST_LENGTH], + } } pub fn make_meaningful_stream_key(phrase: &str) -> StreamKey { diff --git a/node/src/sub_lib/ttl_hashmap.rs b/node/src/sub_lib/ttl_hashmap.rs index 4782e78a3..faa79b68a 100644 --- a/node/src/sub_lib/ttl_hashmap.rs +++ b/node/src/sub_lib/ttl_hashmap.rs @@ -8,7 +8,6 @@ use std::rc::Rc; use std::time::Duration; use std::time::Instant; -#[allow(clippy::type_complexity)] pub struct TtlHashMap where K: Hash + Clone, @@ -16,7 +15,6 @@ where last_check: RefCell, data: RefCell, Instant)>>, ttl: Duration, - retire_closure: Box bool>, } impl TtlHashMap @@ -29,19 +27,6 @@ where last_check: RefCell::new(Instant::now()), data: RefCell::new(HashMap::new()), ttl, - retire_closure: Box::new(|_, _| true), - } - } - - pub fn new_with_retire(ttl: Duration, retire_closure: F) -> TtlHashMap - where - F: 'static + Fn(&K, &V) -> bool, - { - TtlHashMap { - last_check: RefCell::new(Instant::now()), - data: RefCell::new(HashMap::new()), - ttl, - retire_closure: Box::new(retire_closure), } } @@ -69,12 +54,6 @@ where } } - pub fn remove(&self, key: &K) -> Option> { - self.remove_expired_entries(); - - self.data.borrow_mut().remove(key).map(|(result, _)| result) - } - fn remove_expired_entries(&self) { let now = Instant::now(); @@ -94,16 +73,8 @@ where .collect() }; - let mut data = self.data.borrow_mut(); expired.iter().for_each(|key| { - match data.remove(key) { - Some((value, _)) => { - if !(self.retire_closure)(key, value.as_ref()) { - data.insert(key.clone(), (value, now)); - } - } - None => (), // already removed - } + self.data.borrow_mut().remove(key); }); } } @@ -111,70 +82,26 @@ where #[cfg(test)] mod tests { use super::*; - use std::sync::{Arc, Mutex}; use std::thread; #[test] fn new_sets_ttl() { let subject = TtlHashMap::::new(Duration::from_millis(1000)); - let result = subject.ttl; - - assert_eq!(result, Duration::from_millis(1000)); + assert_eq!(subject.ttl, Duration::from_millis(1000)); } #[test] - fn get_returns_none_for_entry_that_was_never_inserted() { + fn remove_returns_none_for_entry_that_was_never_inserted() { let subject = TtlHashMap::::new(Duration::from_millis(1000)); - let result = subject.get(&11u32); - - assert_eq!(result, None); + assert_eq!(subject.get(&11u32), None); assert_eq!(subject.ttl(), Duration::from_millis(1000)); } - #[test] - fn remove_returns_none_if_no_such_entry_exists() { - let subject = TtlHashMap::::new(Duration::from_millis(1000)); - - let result = subject.remove(&11u32); - - assert_eq!(result, None); - } - - #[test] - fn remove_returns_existing_entry_and_removes() { - let mut subject = TtlHashMap::::new(Duration::from_millis(1000)); - subject.insert(11u32, 42u32); - - let before_result = subject.remove(&11u32); - let after_result = subject.remove(&11u32); - - assert_eq!(before_result, Some(Rc::new(42u32))); - assert_eq!(after_result, None); - } - - #[test] - fn ttl_hashmap_remove_removes_expired_entry() { - let mut subject = TtlHashMap::new(Duration::from_millis(10)); - subject.insert(42u32, "Hello"); - thread::sleep(Duration::from_millis(20)); - - let result = subject.remove(&11u32); // nonexistent key - - assert_eq!(result, None); - // Low-level get, because high-level get would remove it if .remove() didn't - assert_eq!(subject.data.borrow().get(&42u32), None); - } - #[test] fn ttl_hashmap_does_not_remove_entry_before_it_is_expired() { - let retire_closure_has_run = Arc::new(Mutex::new(false)); - let retire_closure_has_run_inner = retire_closure_has_run.clone(); - let mut subject = TtlHashMap::new_with_retire(Duration::from_millis(10), move |_, _| { - *(retire_closure_has_run_inner.lock().unwrap()) = true; - true - }); + let mut subject = TtlHashMap::new(Duration::from_millis(10)); subject.insert(42u32, "Hello"); subject.insert(24u32, "World"); @@ -182,52 +109,25 @@ mod tests { assert_eq!(subject.get(&42u32).unwrap().as_ref(), &"Hello"); assert_eq!(subject.get(&24u32).unwrap().as_ref(), &"World"); assert_eq!(subject.ttl(), Duration::from_millis(10)); - assert_eq!(*retire_closure_has_run.lock().unwrap(), false); } #[test] fn ttl_hashmap_get_removes_expired_entry() { - let retire_closure_has_run = Arc::new(Mutex::new(false)); - let retire_closure_has_run_inner = retire_closure_has_run.clone(); - let mut subject = TtlHashMap::new_with_retire(Duration::from_millis(10), move |_, _| { - *(retire_closure_has_run_inner.lock().unwrap()) = true; - true - }); - subject.insert(42u32, "Hello"); - thread::sleep(Duration::from_millis(20)); - - let result = subject.get(&42u32); - - assert_eq!(result, None); - assert_eq!(*retire_closure_has_run.lock().unwrap(), true); - } + let mut subject = TtlHashMap::new(Duration::from_millis(10)); - #[test] - fn ttl_hashmap_get_does_not_remove_expired_entry_if_closure_returns_false() { - let retire_closure_has_run = Arc::new(Mutex::new(false)); - let retire_closure_has_run_inner = retire_closure_has_run.clone(); - let mut subject = TtlHashMap::new_with_retire(Duration::from_millis(10), move |_, _| { - *(retire_closure_has_run_inner.lock().unwrap()) = true; - false - }); subject.insert(42u32, "Hello"); - thread::sleep(Duration::from_millis(20)); - let result = subject.get(&42u32); + thread::sleep(Duration::from_millis(20)); - assert_eq!(result, Some(Rc::new("Hello"))); - assert_eq!(*retire_closure_has_run.lock().unwrap(), true); + assert_eq!(subject.get(&42u32), None); } #[test] fn ttl_hashmap_insert_removes_expired_entry() { - let retire_closure_has_run = Arc::new(Mutex::new(false)); - let retire_closure_has_run_inner = retire_closure_has_run.clone(); - let mut subject = TtlHashMap::new_with_retire(Duration::from_millis(10), move |_, _| { - *(retire_closure_has_run_inner.lock().unwrap()) = true; - true - }); + let mut subject = TtlHashMap::new(Duration::from_millis(10)); + subject.insert(42u32, "Hello"); + thread::sleep(Duration::from_millis(20)); subject.insert(24u32, "World"); @@ -237,7 +137,6 @@ mod tests { subject.data.borrow().get(&24u32).unwrap().0.as_ref(), &"World" ); - assert_eq!(*retire_closure_has_run.lock().unwrap(), true); } #[test] diff --git a/node/src/test_utils/mod.rs b/node/src/test_utils/mod.rs index 73c5e5044..351b27711 100644 --- a/node/src/test_utils/mod.rs +++ b/node/src/test_utils/mod.rs @@ -21,6 +21,7 @@ use crate::blockchain::bip32::Bip32EncryptionKeyProvider; use crate::blockchain::payer::Payer; use crate::bootstrapper::CryptDEPair; use crate::sub_lib::cryptde::CryptDE; +use crate::sub_lib::cryptde::CryptData; use crate::sub_lib::cryptde::PlainData; use crate::sub_lib::cryptde::PublicKey; use crate::sub_lib::dispatcher::Component; @@ -51,10 +52,8 @@ use std::iter::repeat; use std::net::{Shutdown, TcpStream}; use crate::sub_lib::hopper::MessageType; -use crate::sub_lib::host::Host; use crate::sub_lib::proxy_client::DnsResolveFailure_0v1; use crate::sub_lib::stream_key::StreamKey; -use masq_lib::constants::{HTTP_PORT, TLS_PORT}; use std::str::FromStr; use std::sync::{Arc, Mutex}; use std::thread; @@ -196,26 +195,25 @@ pub fn make_meaningless_wallet_private_key() -> PlainData { } // TODO: The three functions below should use only one argument, cryptde -pub fn route_to_proxy_client(main_key: &PublicKey, main_cryptde: &dyn CryptDE, tls: bool) -> Route { +pub fn route_to_proxy_client(main_key: &PublicKey, main_cryptde: &dyn CryptDE) -> Route { shift_one_hop( - zero_hop_route_response(main_key, main_cryptde, tls).route, + zero_hop_route_response(main_key, main_cryptde).route, main_cryptde, ) } -pub fn route_from_proxy_client(key: &PublicKey, cryptde: &dyn CryptDE, tls: bool) -> Route { +pub fn route_from_proxy_client(key: &PublicKey, cryptde: &dyn CryptDE) -> Route { // Happens to be the same - route_to_proxy_client(key, cryptde, tls) + route_to_proxy_client(key, cryptde) } -pub fn route_to_proxy_server(key: &PublicKey, cryptde: &dyn CryptDE, tls: bool) -> Route { - shift_one_hop(route_from_proxy_client(key, cryptde, tls), cryptde) +pub fn route_to_proxy_server(key: &PublicKey, cryptde: &dyn CryptDE) -> Route { + shift_one_hop(route_from_proxy_client(key, cryptde), cryptde) } pub fn zero_hop_route_response( public_key: &PublicKey, cryptde: &dyn CryptDE, - tls: bool, ) -> RouteQueryResponse { RouteQueryResponse { route: Route::round_trip( @@ -223,14 +221,15 @@ pub fn zero_hop_route_response( RouteSegment::new(vec![public_key, public_key], Component::ProxyServer), cryptde, None, + 0, None, ) .unwrap(), expected_services: ExpectedServices::RoundTrip( vec![ExpectedService::Nothing, ExpectedService::Nothing], vec![ExpectedService::Nothing, ExpectedService::Nothing], + 0, ), - host: Host::new("booga.com", if tls { TLS_PORT } else { HTTP_PORT }), } } @@ -239,6 +238,13 @@ fn shift_one_hop(mut route: Route, cryptde: &dyn CryptDE) -> Route { route } +pub fn encrypt_return_route_id(return_route_id: u32, cryptde: &dyn CryptDE) -> CryptData { + let return_route_id_ser = serde_cbor::ser::to_vec(&return_route_id).unwrap(); + cryptde + .encode(cryptde.public_key(), &PlainData::from(return_route_id_ser)) + .unwrap() +} + pub fn make_garbage_data(bytes: usize) -> Vec { let mut data = vec![0; bytes]; rand::thread_rng().fill_bytes(&mut data); @@ -423,8 +429,8 @@ pub fn read_until_timeout(stream: &mut dyn Read) -> Vec { response } -pub fn make_meaningless_message_type(stream_key: StreamKey) -> MessageType { - DnsResolveFailure_0v1::new(stream_key).into() +pub fn make_meaningless_message_type() -> MessageType { + DnsResolveFailure_0v1::new(StreamKey::make_meaningless_stream_key()).into() } pub fn handle_connection_error(stream: TcpStream) { @@ -688,7 +694,7 @@ pub mod unshared_test_utils { ClientRequestPayload_0v1 { stream_key: StreamKey::make_meaningful_stream_key("request"), sequenced_packet: SequencedPacket::new(make_garbage_data(bytes), 0, true), - target_hostname: "www.example.com".to_string(), + target_hostname: Some("www.example.com".to_string()), target_port: HTTP_PORT, protocol: ProxyProtocol::HTTP, originator_public_key: cryptde.public_key().clone(), @@ -1225,7 +1231,7 @@ mod tests { let cryptde = CRYPTDE_PAIR.main.as_ref(); let key = cryptde.public_key(); - let subject = zero_hop_route_response(&key, cryptde, false); + let subject = zero_hop_route_response(&key, cryptde); assert_eq!( subject.route.hops, @@ -1239,6 +1245,7 @@ mod tests { LiveHop::new(&PublicKey::new(b""), None, Component::ProxyServer) .encode(&key, cryptde) .unwrap(), + encrypt_return_route_id(0, cryptde), ) ); assert_eq!( @@ -1246,6 +1253,7 @@ mod tests { ExpectedServices::RoundTrip( vec![ExpectedService::Nothing, ExpectedService::Nothing,], vec![ExpectedService::Nothing, ExpectedService::Nothing,], + 0 ) ); } @@ -1255,7 +1263,7 @@ mod tests { let cryptde = CRYPTDE_PAIR.main.as_ref(); let key = cryptde.public_key(); - let subject = route_to_proxy_client(&key, cryptde, false); + let subject = route_to_proxy_client(&key, cryptde); let mut garbage_can: Vec = iter::repeat(0u8).take(96).collect(); cryptde.random(&mut garbage_can[..]); @@ -1268,6 +1276,7 @@ mod tests { LiveHop::new(&PublicKey::new(b""), None, Component::ProxyServer) .encode(&key, cryptde) .unwrap(), + encrypt_return_route_id(0, cryptde), CryptData::new(&garbage_can[..]) ) ); @@ -1278,7 +1287,7 @@ mod tests { let cryptde = CRYPTDE_PAIR.main.as_ref(); let key = cryptde.public_key(); - let subject = route_from_proxy_client(&key, cryptde, false); + let subject = route_from_proxy_client(&key, cryptde); let mut garbage_can: Vec = iter::repeat(0u8).take(96).collect(); cryptde.random(&mut garbage_can[..]); @@ -1291,6 +1300,7 @@ mod tests { LiveHop::new(&PublicKey::new(b""), None, Component::ProxyServer) .encode(&key, cryptde) .unwrap(), + encrypt_return_route_id(0, cryptde), CryptData::new(&garbage_can[..]) ) ); @@ -1301,7 +1311,7 @@ mod tests { let cryptde = CRYPTDE_PAIR.main.as_ref(); let key = cryptde.public_key(); - let subject = route_to_proxy_server(&key, cryptde, false); + let subject = route_to_proxy_server(&key, cryptde); let mut first_garbage_can: Vec = iter::repeat(0u8).take(96).collect(); let mut second_garbage_can: Vec = iter::repeat(0u8).take(96).collect(); @@ -1313,6 +1323,7 @@ mod tests { LiveHop::new(&PublicKey::new(b""), None, Component::ProxyServer) .encode(&key, cryptde) .unwrap(), + encrypt_return_route_id(0, cryptde), CryptData::new(&first_garbage_can[..]), CryptData::new(&second_garbage_can[..]), ) diff --git a/node/src/test_utils/recorder.rs b/node/src/test_utils/recorder.rs index 20daaec86..f66125182 100644 --- a/node/src/test_utils/recorder.rs +++ b/node/src/test_utils/recorder.rs @@ -39,8 +39,10 @@ use crate::sub_lib::peer_actors::PeerActors; use crate::sub_lib::peer_actors::{BindMessage, NewPublicIp, StartMessage}; use crate::sub_lib::proxy_client::{ClientResponsePayload_0v1, InboundServerData}; use crate::sub_lib::proxy_client::{DnsResolveFailure_0v1, ProxyClientSubs}; +use crate::sub_lib::proxy_server::{ + AddReturnRouteMessage, ClientRequestPayload_0v1, StreamKeyPurge, +}; use crate::sub_lib::proxy_server::{AddRouteResultMessage, ProxyServerSubs}; -use crate::sub_lib::proxy_server::{ClientRequestPayload_0v1, StreamKeyPurge}; use crate::sub_lib::stream_handler_pool::DispatcherNodeQueryResponse; use crate::sub_lib::stream_handler_pool::TransmitDataMsg; use crate::sub_lib::ui_gateway::UiGatewaySubs; @@ -121,6 +123,7 @@ macro_rules! recorder_message_handler_t_p { }; } +recorder_message_handler_t_m_p!(AddReturnRouteMessage); recorder_message_handler_t_m_p!(AddRouteResultMessage); recorder_message_handler_t_p!(AddStreamMsg); recorder_message_handler_t_m_p!(BindMessage); @@ -395,6 +398,7 @@ pub fn make_proxy_server_subs_from_recorder(addr: &Addr) -> ProxyServe from_dispatcher: recipient!(addr, InboundClientData), from_hopper: recipient!(addr, ExpiredCoresPackage), dns_failure_from_hopper: recipient!(addr, ExpiredCoresPackage), + add_return_route: recipient!(addr, AddReturnRouteMessage), stream_shutdown_sub: recipient!(addr, StreamShutdownMsg), node_from_ui: recipient!(addr, NodeFromUiMessage), route_result_sub: recipient!(addr, AddRouteResultMessage), From 42eebccfaab966d564b3cc889d20c39173013178 Mon Sep 17 00:00:00 2001 From: KauriHero Date: Tue, 11 Nov 2025 12:10:37 +1300 Subject: [PATCH 14/20] Update ci-matrix to test new runners (#741) * update ci-matrix to test new runners * Revert Windows runner in CI matrix Moving windows runner back to 2022, so we can allow macos-latest to fully run through Actions --- .github/workflows/ci-matrix.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/ci-matrix.yml b/.github/workflows/ci-matrix.yml index 5285afcbd..b444643e8 100644 --- a/.github/workflows/ci-matrix.yml +++ b/.github/workflows/ci-matrix.yml @@ -14,7 +14,7 @@ jobs: matrix: target: - { name: linux, os: ubuntu-22.04 } - - { name: macos, os: macos-13 } + - { name: macos, os: macos-latest } - { name: windows, os: windows-2022 } name: Build node on ${{ matrix.target.os }} From e82da7774c95d0fc09fd409bf2ff9a7a18ea4c25 Mon Sep 17 00:00:00 2001 From: KauriHero Date: Thu, 13 Nov 2025 21:26:45 +1300 Subject: [PATCH 15/20] Refine regex for max block count extraction and add test (#735) * Refine regex for max block count extraction and add test Updated regex to capture maximum block count from error messages more accurately - namely update to Nodies provider. Added a new test case for extracting this specific error response. * Fix inclusive range calculation for block scanning in BlockchainInterfaceWeb3 Adjusted the end block marker calculation to correctly account for inclusive ranges by subtracting 1 from the scan range. T * Fix block marker calculations in tests for blockchain bridge and interface. Adjusted expected new start block values to ensure accurate transaction retrieval and logging. --- node/src/blockchain/blockchain_bridge.rs | 17 +++++++++++++---- .../blockchain_interface_web3/mod.rs | 12 +++++++----- 2 files changed, 20 insertions(+), 9 deletions(-) diff --git a/node/src/blockchain/blockchain_bridge.rs b/node/src/blockchain/blockchain_bridge.rs index a0e501393..e5b164279 100644 --- a/node/src/blockchain/blockchain_bridge.rs +++ b/node/src/blockchain/blockchain_bridge.rs @@ -507,8 +507,8 @@ impl BlockchainBridge { pub fn extract_max_block_count(error: BlockchainError) -> Option { let regex_result = - Regex::new(r".* (max: |allowed for your plan: |is limited to |block range limit \(|exceeds max block range )(?P\d+).*") - .expect("Invalid regex"); + Regex::new(r".* (max: |allowed for your plan: |is limited to |block range limit \(|exceeds max block range |maximum allowed is )(?P\d+).*") + .expect("Invalid regex"); let max_block_count = match error { BlockchainError::QueryFailed(msg) => match regex_result.captures(msg.as_str()) { Some(captures) => match captures.name("max_block_count") { @@ -1541,7 +1541,7 @@ mod tests { system.run(); let after = SystemTime::now(); let expected_transactions = RetrievedBlockchainTransactions { - new_start_block: BlockMarker::Value(42 + 9_000_000 + 1), + new_start_block: BlockMarker::Value(42 + 9_000_000), transactions: vec![ BlockchainTransaction { block_number: 6040059, @@ -1742,7 +1742,7 @@ mod tests { let received_payments_message = accountant_recording.get_record::(0); check_timestamp(before, received_payments_message.timestamp, after); let expected_transactions = RetrievedBlockchainTransactions { - new_start_block: BlockMarker::Value(6 + 5000 + 1), + new_start_block: BlockMarker::Value(6 + 5000), transactions: vec![BlockchainTransaction { block_number: 2000, from: earning_wallet.clone(), @@ -2201,6 +2201,15 @@ mod tests { assert_eq!(Some(100000), max_block_count); } + #[test] + fn extract_max_block_range_for_nodies_error_response_v2() { + let result = BlockchainError::QueryFailed("RPC error: Error { code: ServerError(-32001), message: \"Block range too large: maximum allowed is 20000 blocks\", data: None }".to_string()); + + let max_block_count = BlockchainBridge::extract_max_block_count(result); + + assert_eq!(Some(20000), max_block_count); + } + #[test] fn extract_max_block_range_for_expected_batch_got_single_error_response() { let result = BlockchainError::QueryFailed( diff --git a/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs b/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs index 92f8e9145..3971555d1 100644 --- a/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs +++ b/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs @@ -367,7 +367,9 @@ impl BlockchainInterfaceWeb3 { ) -> BlockMarker { let locally_determined_end_block_marker = match (start_block_marker, scan_range) { (BlockMarker::Value(start_block), BlockScanRange::Range(scan_range_number)) => { - BlockMarker::Value(start_block + scan_range_number) + // Subtract 1 because the range is inclusive: [start_block, end_block] + // Example: If max range is 20000, we need start_block to start_block+20000-1 (ending up with 20000 blocks total) + BlockMarker::Value(start_block + scan_range_number - 1) } (_, _) => BlockMarker::Uninitialized, }; @@ -515,8 +517,8 @@ mod tests { let start_block_marker = BlockMarker::Value(42); let scan_range = BlockScanRange::Range(1000); let block_response = "0x7d0"; // 2_000 - let expected_new_start_block = BlockMarker::Value(42 + 1000 + 1); - let expected_log = "from start block: Number(42) to end block: Number(1042)"; + let expected_new_start_block = BlockMarker::Value(42 + 1000); + let expected_log = "from start block: Number(42) to end block: Number(1041)"; assert_on_retrieves_transactions( start_block_marker, scan_range, @@ -1176,7 +1178,7 @@ mod tests { Err(BlockchainError::InvalidResponse), &logger ), - BlockMarker::Value(150) + BlockMarker::Value(149) ); assert_eq!( Subject::calculate_end_block_marker( @@ -1194,7 +1196,7 @@ mod tests { Ok(120.into()), &logger ), - BlockMarker::Value(50 + 10) + BlockMarker::Value(59) ); } From 858adfa8b4e8b64c21637783b425b600d3f99d76 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Wed, 19 Nov 2025 11:28:26 +0100 Subject: [PATCH 16/20] GH-598-hot-fix-panic-bug: fixed (#746) --- node/src/blockchain/blockchain_bridge.rs | 83 +++++++++++++----------- 1 file changed, 44 insertions(+), 39 deletions(-) diff --git a/node/src/blockchain/blockchain_bridge.rs b/node/src/blockchain/blockchain_bridge.rs index 183f58659..d892409dc 100644 --- a/node/src/blockchain/blockchain_bridge.rs +++ b/node/src/blockchain/blockchain_bridge.rs @@ -312,28 +312,32 @@ impl BlockchainBridge { Box::new( self.process_payments(msg.agent, msg.priced_templates) - .map_err(move |e: LocalPayableError| { - sent_payable_subs_err - .try_send(SentPayables { - payment_procedure_result: Self::payment_procedure_result_from_error( - e.clone(), - ), - payable_scan_type, - response_skeleton_opt: skeleton_opt, - }) - .expect("Accountant is dead"); - - format!("ReportAccountsPayable: {}", e) - }) - .and_then(move |batch_results| { - sent_payable_subs_success - .try_send(SentPayables { - payment_procedure_result: Ok(batch_results), - payable_scan_type, - response_skeleton_opt: skeleton_opt, - }) - .expect("Accountant is dead"); - + .then(move |result| { + match result { + Ok(batch_results) => { + sent_payable_subs_success + .try_send(SentPayables { + payment_procedure_result: Ok(batch_results), + payable_scan_type, + response_skeleton_opt: skeleton_opt, + }) + .expect("Accountant is dead"); + } + Err(e) => { + sent_payable_subs_err + .try_send(SentPayables { + payment_procedure_result: + Self::payment_procedure_result_from_error(e), + payable_scan_type, + response_skeleton_opt: skeleton_opt, + }) + .expect("Accountant is dead"); + } + } + // TODO Temporary workaround: prevents the Accountant from receiving two messages + // describing the same error. Duplicate notifications could previously trigger + // a panic in the scanners, because the substitution call for a given scanner + // was executed twice and tripped the guard that enforces a single concurrent scan. Ok(()) }), ) @@ -1021,7 +1025,7 @@ mod tests { // let pending_payable_fingerprint_seeds_msg = // accountant_recording.get_record::(0); let sent_payables_msg = accountant_recording.get_record::(0); - let scan_error_msg = accountant_recording.get_record::(1); + // let scan_error_msg = accountant_recording.get_record::(1); let batch_results = sent_payables_msg.clone().payment_procedure_result.unwrap(); let failed_tx = FailedTx { hash: H256::from_str( @@ -1048,22 +1052,23 @@ mod tests { // amount: account.balance_wei // }] // ); - assert_eq!(scan_error_msg.scan_type, DetailedScanType::NewPayables); - assert_eq!( - scan_error_msg.response_skeleton_opt, - Some(ResponseSkeleton { - client_id: 1234, - context_id: 4321 - }) - ); - assert!(scan_error_msg - .msg - .contains("ReportAccountsPayable: Sending error: \"Transport error: Error(IncompleteMessage)\". Signed and hashed transactions:"), "This string didn't contain the expected: {}", scan_error_msg.msg); - assert!(scan_error_msg.msg.contains( - "FailedTx { hash: 0x81d20df32920161727cd20e375e53c2f9df40fd80256a236fb39e444c999fb6c," - )); - assert!(scan_error_msg.msg.contains("FailedTx { hash: 0x81d20df32920161727cd20e375e53c2f9df40fd80256a236fb39e444c999fb6c, receiver_address: 0x00000000000000000000000000000000626c6168, amount_minor: 111420204, timestamp:"), "This string didn't contain the expected: {}", scan_error_msg.msg); - assert_eq!(accountant_recording.len(), 2); + // assert_eq!(scan_error_msg.scan_type, DetailedScanType::NewPayables); + // assert_eq!( + // scan_error_msg.response_skeleton_opt, + // Some(ResponseSkeleton { + // client_id: 1234, + // context_id: 4321 + // }) + // ); + // assert!(scan_error_msg + // .msg + // .contains("ReportAccountsPayable: Sending error: \"Transport error: Error(IncompleteMessage)\". Signed and hashed transactions:"), "This string didn't contain the expected: {}", scan_error_msg.msg); + // assert!(scan_error_msg.msg.contains( + // "FailedTx { hash: 0x81d20df32920161727cd20e375e53c2f9df40fd80256a236fb39e444c999fb6c," + // )); + // assert!(scan_error_msg.msg.contains("FailedTx { hash: 0x81d20df32920161727cd20e375e53c2f9df40fd80256a236fb39e444c999fb6c, receiver_address: 0x00000000000000000000000000000000626c6168, amount_minor: 111420204, timestamp:"), "This string didn't contain the expected: {}", scan_error_msg.msg); + //assert_eq!(accountant_recording.len(), 2); + assert_eq!(accountant_recording.len(), 1); } #[test] From 6dab7987314caf1deb17ddf24a5723fbf146a154 Mon Sep 17 00:00:00 2001 From: Bert Date: Thu, 20 Nov 2025 13:44:37 +0100 Subject: [PATCH 17/20] GH-598: fixed a flaw from last merge --- node/src/blockchain/blockchain_bridge.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/node/src/blockchain/blockchain_bridge.rs b/node/src/blockchain/blockchain_bridge.rs index cf0590f36..8ced8b33e 100644 --- a/node/src/blockchain/blockchain_bridge.rs +++ b/node/src/blockchain/blockchain_bridge.rs @@ -2205,7 +2205,7 @@ mod tests { #[test] fn extract_max_block_range_for_nodies_error_response_v2() { - let result = BlockchainError::QueryFailed("RPC error: Error { code: ServerError(-32001), message: \"Block range too large: maximum allowed is 20000 blocks\", data: None }".to_string()); + let result = BlockchainInterfaceError::QueryFailed("RPC error: Error { code: ServerError(-32001), message: \"Block range too large: maximum allowed is 20000 blocks\", data: None }".to_string()); let max_block_count = BlockchainBridge::extract_max_block_count(result); From 1ba4317b6cc90af5a001b56eb501065bbde08c54 Mon Sep 17 00:00:00 2001 From: Utkarsh Gupta <32920299+utkarshg6@users.noreply.github.com> Date: Fri, 12 Dec 2025 05:21:05 +0530 Subject: [PATCH 18/20] GH-754: Gas Price Optimisation - Hot Fix (#751) * GH-827: add boilerplate code * GH-827: introduce new logic * GH-827: some renamings for clarity * GH-827: fix test estimate_transaction_fee_total_works_for_retry_txs * GH-827: refactor test estimate_transaction_fee_total_works_for_retry_txs * GH-827: fix test returns_correct_priced_qualified_payables_for_retry_payable_scan * GH-827: fix test blockchain_interface_web3_can_introduce_blockchain_agent_in_the_retry_payables_mode * GH-827: fix 2 more tests * GH-827: refactor retry_payables_gas_price_ceiling_test_of_border_value_if_the_previous_attempt_being_bigger * GH-827: review changes --- masq_lib/src/constants.rs | 6 +- .../tx_templates/priced/new.rs | 4 +- .../tx_templates/priced/retry.rs | 56 +++++- .../blockchain/blockchain_agent/agent_web3.rs | 165 +++++++++--------- node/src/blockchain/blockchain_bridge.rs | 12 +- .../blockchain_interface_web3/mod.rs | 21 +-- .../blockchain_interface_initializer.rs | 4 +- 7 files changed, 160 insertions(+), 108 deletions(-) diff --git a/masq_lib/src/constants.rs b/masq_lib/src/constants.rs index 20e332f4a..1be85f381 100644 --- a/masq_lib/src/constants.rs +++ b/masq_lib/src/constants.rs @@ -83,7 +83,8 @@ pub const VALUE_EXCEEDS_ALLOWED_LIMIT: u64 = ACCOUNTANT_PREFIX | 3; pub const MASQ_TOTAL_SUPPLY: u64 = 37_500_000; pub const DEFAULT_GAS_PRICE: u64 = 1; //TODO ?? Really -pub const DEFAULT_GAS_PRICE_MARGIN: u64 = 30; +pub const DEFAULT_GAS_PRICE_RETRY_PERCENTAGE: u64 = 30; +pub const DEFAULT_GAS_PRICE_RETRY_CONSTANT: u128 = 5_000; pub const DEFAULT_MAX_BLOCK_COUNT: u64 = 100_000; //chains @@ -142,7 +143,8 @@ mod tests { assert_eq!(CURRENT_LOGFILE_NAME, "MASQNode_rCURRENT.log"); assert_eq!(MASQ_PROMPT, "masq> "); assert_eq!(DEFAULT_GAS_PRICE, 1); - assert_eq!(DEFAULT_GAS_PRICE_MARGIN, 30); + assert_eq!(DEFAULT_GAS_PRICE_RETRY_PERCENTAGE, 30); + assert_eq!(DEFAULT_GAS_PRICE_RETRY_CONSTANT, 5_000); assert_eq!(WALLET_ADDRESS_LENGTH, 42); assert_eq!(MASQ_TOTAL_SUPPLY, 37_500_000); assert_eq!(WEIS_IN_GWEI, 1_000_000_000); diff --git a/node/src/accountant/scanners/payable_scanner/tx_templates/priced/new.rs b/node/src/accountant/scanners/payable_scanner/tx_templates/priced/new.rs index 6de54e4c9..200b9602b 100644 --- a/node/src/accountant/scanners/payable_scanner/tx_templates/priced/new.rs +++ b/node/src/accountant/scanners/payable_scanner/tx_templates/priced/new.rs @@ -4,7 +4,7 @@ use crate::accountant::scanners::payable_scanner::tx_templates::initial::new::{ NewTxTemplate, NewTxTemplates, }; use crate::accountant::scanners::payable_scanner::tx_templates::BaseTxTemplate; -use crate::blockchain::blockchain_bridge::increase_gas_price_by_margin; +use crate::blockchain::blockchain_bridge::increase_by_percentage; use masq_lib::logger::Logger; use std::ops::Deref; use thousands::Separable; @@ -63,7 +63,7 @@ impl PricedNewTxTemplates { ceil: u128, logger: &Logger, ) -> Self { - let computed_gas_price_wei = increase_gas_price_by_margin(latest_gas_price_wei); + let computed_gas_price_wei = increase_by_percentage(latest_gas_price_wei); let safe_gas_price_wei = if computed_gas_price_wei > ceil { warning!( diff --git a/node/src/accountant/scanners/payable_scanner/tx_templates/priced/retry.rs b/node/src/accountant/scanners/payable_scanner/tx_templates/priced/retry.rs index 48e41f4b9..3477b206a 100644 --- a/node/src/accountant/scanners/payable_scanner/tx_templates/priced/retry.rs +++ b/node/src/accountant/scanners/payable_scanner/tx_templates/priced/retry.rs @@ -4,7 +4,8 @@ use crate::accountant::scanners::payable_scanner::tx_templates::initial::retry:: RetryTxTemplate, RetryTxTemplates, }; use crate::accountant::scanners::payable_scanner::tx_templates::BaseTxTemplate; -use crate::blockchain::blockchain_bridge::increase_gas_price_by_margin; +use crate::blockchain::blockchain_bridge::increase_by_percentage; +use masq_lib::constants::DEFAULT_GAS_PRICE_RETRY_CONSTANT; use masq_lib::logger::Logger; use std::ops::{Deref, DerefMut}; use thousands::Separable; @@ -34,7 +35,7 @@ impl PricedRetryTxTemplate { ) -> PricedRetryTxTemplate { let receiver = retry_tx_template.base.receiver_address; let computed_gas_price_wei = - Self::compute_gas_price(retry_tx_template.prev_gas_price_wei, latest_gas_price_wei); + Self::compute_gas_price(latest_gas_price_wei, retry_tx_template.prev_gas_price_wei); let safe_gas_price_wei = if computed_gas_price_wei > ceil { log_builder.push(receiver, computed_gas_price_wei); @@ -46,10 +47,12 @@ impl PricedRetryTxTemplate { PricedRetryTxTemplate::new(retry_tx_template, safe_gas_price_wei) } - fn compute_gas_price(latest_gas_price_wei: u128, prev_gas_price_wei: u128) -> u128 { - let gas_price_wei = latest_gas_price_wei.max(prev_gas_price_wei); - - increase_gas_price_by_margin(gas_price_wei) + pub fn compute_gas_price(latest_gas_price_wei: u128, prev_gas_price_wei: u128) -> u128 { + if latest_gas_price_wei >= prev_gas_price_wei { + increase_by_percentage(latest_gas_price_wei) + } else { + prev_gas_price_wei + DEFAULT_GAS_PRICE_RETRY_CONSTANT + } } } @@ -167,3 +170,44 @@ impl RetryLogBuilder { } } } + +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn compute_gas_price_increases_by_percentage_if_latest_is_higher() { + let latest_gas_price_wei = 101; + let prev_gas_price_wei = 100; + + let computed_gas_price = + PricedRetryTxTemplate::compute_gas_price(latest_gas_price_wei, prev_gas_price_wei); + + let expected_gas_price = increase_by_percentage(latest_gas_price_wei); + assert_eq!(computed_gas_price, expected_gas_price); + } + + #[test] + fn compute_gas_price_increases_by_percentage_if_latest_is_equal() { + let latest_gas_price_wei = 100; + let prev_gas_price_wei = 100; + + let computed_gas_price = + PricedRetryTxTemplate::compute_gas_price(latest_gas_price_wei, prev_gas_price_wei); + + let expected_gas_price = increase_by_percentage(latest_gas_price_wei); + assert_eq!(computed_gas_price, expected_gas_price); + } + + #[test] + fn compute_gas_price_increments_previous_by_constant_if_latest_is_lower() { + let latest_gas_price_wei = 99; + let prev_gas_price_wei = 100; + + let computed_gas_price = + PricedRetryTxTemplate::compute_gas_price(latest_gas_price_wei, prev_gas_price_wei); + + let expected_gas_price = prev_gas_price_wei + DEFAULT_GAS_PRICE_RETRY_CONSTANT; + assert_eq!(computed_gas_price, expected_gas_price); + } +} diff --git a/node/src/blockchain/blockchain_agent/agent_web3.rs b/node/src/blockchain/blockchain_agent/agent_web3.rs index 66df08d57..4446642cd 100644 --- a/node/src/blockchain/blockchain_agent/agent_web3.rs +++ b/node/src/blockchain/blockchain_agent/agent_web3.rs @@ -118,11 +118,13 @@ mod tests { BlockchainAgentWeb3, WEB3_MAXIMAL_GAS_LIMIT_MARGIN, }; use crate::blockchain::blockchain_agent::BlockchainAgent; - use crate::blockchain::blockchain_bridge::increase_gas_price_by_margin; + use crate::blockchain::blockchain_bridge::increase_by_percentage; use crate::test_utils::make_wallet; use itertools::{Either, Itertools}; use masq_lib::blockchains::chains::Chain; - use masq_lib::constants::DEFAULT_GAS_PRICE_MARGIN; + use masq_lib::constants::{ + DEFAULT_GAS_PRICE_RETRY_CONSTANT, DEFAULT_GAS_PRICE_RETRY_PERCENTAGE, + }; use masq_lib::logger::Logger; use masq_lib::test_utils::logging::{init_test_logging, TestLogHandler}; use masq_lib::test_utils::utils::TEST_DEFAULT_CHAIN; @@ -155,7 +157,7 @@ mod tests { let result = subject.price_qualified_payables(Either::Left(new_tx_templates.clone())); - let gas_price_with_margin_wei = increase_gas_price_by_margin(rpc_gas_price_wei); + let gas_price_with_margin_wei = increase_by_percentage(rpc_gas_price_wei); let expected_result = Either::Left(PricedNewTxTemplates::new( new_tx_templates, gas_price_with_margin_wei, @@ -170,30 +172,32 @@ mod tests { let test_name = "returns_correct_priced_qualified_payables_for_retry_payable_scan"; let consuming_wallet = make_wallet("efg"); let consuming_wallet_balances = make_zeroed_consuming_wallet_balances(); - let rpc_gas_price_wei = 444_555_666; + let latest_gas_price_wei = 444_555_666; let chain = TEST_DEFAULT_CHAIN; + let prev_gas_prices = vec![ + latest_gas_price_wei - 1, + latest_gas_price_wei, + latest_gas_price_wei + 1, + latest_gas_price_wei - 123_456, + latest_gas_price_wei + 456_789, + ]; let retry_tx_templates: Vec = { - vec![ - rpc_gas_price_wei - 1, - rpc_gas_price_wei, - rpc_gas_price_wei + 1, - rpc_gas_price_wei - 123_456, - rpc_gas_price_wei + 456_789, - ] - .into_iter() - .enumerate() - .map(|(idx, prev_gas_price_wei)| { - let account = make_payable_account((idx as u64 + 1) * 3_000); - RetryTxTemplate { - base: BaseTxTemplate::from(&account), - prev_gas_price_wei, - prev_nonce: idx as u64, - } - }) - .collect_vec() + prev_gas_prices + .iter() + .copied() + .enumerate() + .map(|(idx, prev_gas_price_wei)| { + let account = make_payable_account((idx as u64 + 1) * 3_000); + RetryTxTemplate { + base: BaseTxTemplate::from(&account), + prev_gas_price_wei, + prev_nonce: idx as u64, + } + }) + .collect_vec() }; let mut subject = BlockchainAgentWeb3::new( - rpc_gas_price_wei, + latest_gas_price_wei, 77_777, consuming_wallet, consuming_wallet_balances, @@ -205,23 +209,27 @@ mod tests { .price_qualified_payables(Either::Right(RetryTxTemplates(retry_tx_templates.clone()))); let expected_result = { - let price_wei_for_accounts_from_1_to_5 = vec![ - increase_gas_price_by_margin(rpc_gas_price_wei), - increase_gas_price_by_margin(rpc_gas_price_wei), - increase_gas_price_by_margin(rpc_gas_price_wei + 1), - increase_gas_price_by_margin(rpc_gas_price_wei), - increase_gas_price_by_margin(rpc_gas_price_wei + 456_789), - ]; - if price_wei_for_accounts_from_1_to_5.len() != retry_tx_templates.len() { + let computed_gas_prices = prev_gas_prices + .into_iter() + .map(|prev_gas_price_wei| { + PricedRetryTxTemplate::compute_gas_price( + latest_gas_price_wei, + prev_gas_price_wei, + ) + }) + .collect::>(); + if computed_gas_prices.len() != retry_tx_templates.len() { panic!("Corrupted test") } - Either::Right(PricedRetryTxTemplates( retry_tx_templates .iter() - .zip(price_wei_for_accounts_from_1_to_5.into_iter()) - .map(|(retry_tx_template, increased_gas_price)| { - PricedRetryTxTemplate::new(retry_tx_template.clone(), increased_gas_price) + .zip(computed_gas_prices.into_iter()) + .map(|(retry_tx_template, computed_gas_price_wei)| { + PricedRetryTxTemplate::new( + retry_tx_template.clone(), + computed_gas_price_wei, + ) }) .collect_vec(), )) @@ -238,9 +246,10 @@ mod tests { // This should be the value that would surplus the ceiling just slightly if the margin is // applied. // Adding just 1 didn't work, therefore 2 - let rpc_gas_price_wei = - ((ceiling_gas_price_wei * 100) / (DEFAULT_GAS_PRICE_MARGIN as u128 + 100)) + 2; - let check_value_wei = increase_gas_price_by_margin(rpc_gas_price_wei); + let rpc_gas_price_wei = ((ceiling_gas_price_wei * 100) + / (DEFAULT_GAS_PRICE_RETRY_PERCENTAGE as u128 + 100)) + + 2; + let check_value_wei = increase_by_percentage(rpc_gas_price_wei); test_gas_price_must_not_break_through_ceiling_value_in_the_new_payable_mode( test_name, @@ -340,8 +349,8 @@ mod tests { // applied. // Adding just 1 didn't work, therefore 2 let rpc_gas_price_wei = - (ceiling_gas_price_wei * 100) / (DEFAULT_GAS_PRICE_MARGIN as u128 + 100) + 2; - let check_value_wei = increase_gas_price_by_margin(rpc_gas_price_wei); + (ceiling_gas_price_wei * 100) / (DEFAULT_GAS_PRICE_RETRY_PERCENTAGE as u128 + 100) + 2; + let check_value_wei = increase_by_percentage(rpc_gas_price_wei); let template_1 = RetryTxTemplateBuilder::new() .payable_account(&account_1) .prev_gas_price_wei(rpc_gas_price_wei - 1) @@ -382,18 +391,17 @@ mod tests { let account_1 = make_payable_account(12); let account_2 = make_payable_account(34); let ceiling_gas_price_wei = chain.rec().gas_price_safe_ceiling_minor; - // This should be the value that would surplus the ceiling just slightly if the margin is applied - let border_gas_price_wei = - (ceiling_gas_price_wei * 100) / (DEFAULT_GAS_PRICE_MARGIN as u128 + 100) + 2; - let rpc_gas_price_wei = border_gas_price_wei - 1; - let check_value_wei = increase_gas_price_by_margin(border_gas_price_wei); + // Once the gas price is computed from latest and prev gas price values, it'll break the ceiling + let prev_gas_price_wei = ceiling_gas_price_wei + 1 - DEFAULT_GAS_PRICE_RETRY_CONSTANT; + let latest_gas_price_wei = prev_gas_price_wei - 1; + let check_value_wei = prev_gas_price_wei + DEFAULT_GAS_PRICE_RETRY_CONSTANT; let template_1 = RetryTxTemplateBuilder::new() .payable_account(&account_1) - .prev_gas_price_wei(border_gas_price_wei) + .prev_gas_price_wei(prev_gas_price_wei) .build(); let template_2 = RetryTxTemplateBuilder::new() .payable_account(&account_2) - .prev_gas_price_wei(border_gas_price_wei) + .prev_gas_price_wei(prev_gas_price_wei) .build(); let retry_tx_templates = vec![template_1, template_2]; let expected_log_msg = format!( @@ -406,7 +414,7 @@ mod tests { test_gas_price_must_not_break_through_ceiling_value_in_the_retry_payable_mode( test_name, chain, - rpc_gas_price_wei, + latest_gas_price_wei, Either::Right(RetryTxTemplates(retry_tx_templates)), &expected_log_msg, ); @@ -466,8 +474,8 @@ mod tests { let expected_log_msg = format!( "The computed gas price(s) in wei is above the ceil value of 50,000,000,000 wei computed by this Node.\n\ Transaction(s) to following receivers are affected:\n\ - 0x00000000000000000000000077616c6c65743132 with gas price 64,999,999,998\n\ - 0x00000000000000000000000077616c6c65743334 with gas price 64,999,999,997" + 0x00000000000000000000000077616c6c65743132 with gas price 50,000,004,999\n\ + 0x00000000000000000000000077616c6c65743334 with gas price 50,000,004,998" ); test_gas_price_must_not_break_through_ceiling_value_in_the_retry_payable_mode( @@ -602,8 +610,7 @@ mod tests { assert_eq!( result, - (2 * (77_777 + WEB3_MAXIMAL_GAS_LIMIT_MARGIN)) - * increase_gas_price_by_margin(444_555_666) + (2 * (77_777 + WEB3_MAXIMAL_GAS_LIMIT_MARGIN)) * increase_by_percentage(444_555_666) ); } @@ -611,30 +618,32 @@ mod tests { fn estimate_transaction_fee_total_works_for_retry_txs() { let consuming_wallet = make_wallet("efg"); let consuming_wallet_balances = make_zeroed_consuming_wallet_balances(); - let rpc_gas_price_wei = 444_555_666; + let latest_gas_price_wei = 444_555_666; let chain = TEST_DEFAULT_CHAIN; + let prev_gas_prices = vec![ + latest_gas_price_wei - 1, + latest_gas_price_wei, + latest_gas_price_wei + 1, + latest_gas_price_wei - 123_456, + latest_gas_price_wei + 456_789, + ]; let retry_tx_templates: Vec = { - vec![ - rpc_gas_price_wei - 1, - rpc_gas_price_wei, - rpc_gas_price_wei + 1, - rpc_gas_price_wei - 123_456, - rpc_gas_price_wei + 456_789, - ] - .into_iter() - .enumerate() - .map(|(idx, prev_gas_price_wei)| { - let account = make_payable_account((idx as u64 + 1) * 3_000); - RetryTxTemplate { - base: BaseTxTemplate::from(&account), - prev_gas_price_wei, - prev_nonce: idx as u64, - } - }) - .collect() + prev_gas_prices + .iter() + .copied() + .enumerate() + .map(|(idx, prev_gas_price_wei)| { + let account = make_payable_account((idx as u64 + 1) * 3_000); + RetryTxTemplate { + base: BaseTxTemplate::from(&account), + prev_gas_price_wei, + prev_nonce: idx as u64, + } + }) + .collect() }; let subject = BlockchainAgentWeb3::new( - rpc_gas_price_wei, + latest_gas_price_wei, 77_777, consuming_wallet, consuming_wallet_balances, @@ -645,15 +654,11 @@ mod tests { let result = subject.estimate_transaction_fee_total(&priced_qualified_payables); - let gas_prices_for_accounts_from_1_to_5 = vec![ - increase_gas_price_by_margin(rpc_gas_price_wei), - increase_gas_price_by_margin(rpc_gas_price_wei), - increase_gas_price_by_margin(rpc_gas_price_wei + 1), - increase_gas_price_by_margin(rpc_gas_price_wei), - increase_gas_price_by_margin(rpc_gas_price_wei + 456_789), - ]; - let expected_result = gas_prices_for_accounts_from_1_to_5 + let expected_result = prev_gas_prices .into_iter() + .map(|prev_gas_price_wei| { + PricedRetryTxTemplate::compute_gas_price(latest_gas_price_wei, prev_gas_price_wei) + }) .sum::() * (77_777 + WEB3_MAXIMAL_GAS_LIMIT_MARGIN); assert_eq!(result, expected_result) diff --git a/node/src/blockchain/blockchain_bridge.rs b/node/src/blockchain/blockchain_bridge.rs index 8ced8b33e..0d13ead5d 100644 --- a/node/src/blockchain/blockchain_bridge.rs +++ b/node/src/blockchain/blockchain_bridge.rs @@ -39,7 +39,7 @@ use actix::{Addr, Recipient}; use futures::Future; use itertools::{Either, Itertools}; use masq_lib::blockchains::chains::Chain; -use masq_lib::constants::DEFAULT_GAS_PRICE_MARGIN; +use masq_lib::constants::DEFAULT_GAS_PRICE_RETRY_PERCENTAGE; use masq_lib::logger::Logger; use masq_lib::ui_gateway::NodeFromUiMessage; use regex::Regex; @@ -542,8 +542,8 @@ struct PendingTxInfo { when_sent: SystemTime, } -pub fn increase_gas_price_by_margin(gas_price: u128) -> u128 { - (gas_price * (100 + DEFAULT_GAS_PRICE_MARGIN as u128)) / 100 +pub fn increase_by_percentage(gas_price: u128) -> u128 { + (gas_price * (100 + DEFAULT_GAS_PRICE_RETRY_PERCENTAGE as u128)) / 100 } pub struct BlockchainBridgeSubsFactoryReal {} @@ -775,7 +775,7 @@ mod tests { let accountant_received_payment = accountant_recording_arc.lock().unwrap(); let blockchain_agent_with_context_msg_actual: &PricedTemplatesMessage = accountant_received_payment.get_record(0); - let computed_gas_price_wei = increase_gas_price_by_margin(0x230000000); + let computed_gas_price_wei = increase_by_percentage(0x230000000); let expected_tx_templates = tx_templates .iter() .map(|tx_template| PricedNewTxTemplate { @@ -2239,7 +2239,7 @@ mod tests { #[test] fn increase_gas_price_by_margin_works() { - assert_eq!(increase_gas_price_by_margin(1_000_000_000), 1_300_000_000); - assert_eq!(increase_gas_price_by_margin(9_000_000_000), 11_700_000_000); + assert_eq!(increase_by_percentage(1_000_000_000), 1_300_000_000); + assert_eq!(increase_by_percentage(9_000_000_000), 11_700_000_000); } } diff --git a/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs b/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs index 6f84cd0e9..f7e779cc3 100644 --- a/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs +++ b/node/src/blockchain/blockchain_interface/blockchain_interface_web3/mod.rs @@ -473,7 +473,6 @@ mod tests { use super::*; use crate::accountant::scanners::pending_payable_scanner::utils::TxHashByTable; use crate::accountant::test_utils::make_payable_account; - use crate::blockchain::blockchain_bridge::increase_gas_price_by_margin; use crate::blockchain::blockchain_interface::blockchain_interface_web3::{ BlockchainInterfaceWeb3, CONTRACT_ABI, REQUESTS_IN_PARALLEL, TRANSACTION_LITERAL, TRANSFER_METHOD_ID, @@ -505,10 +504,12 @@ mod tests { use itertools::Either; use web3::transports::Http; use web3::types::{H256, U256}; + use masq_lib::constants::DEFAULT_GAS_PRICE_RETRY_CONSTANT; use crate::accountant::scanners::payable_scanner::tx_templates::initial::new::NewTxTemplates; use crate::accountant::scanners::payable_scanner::tx_templates::initial::retry::RetryTxTemplates; use crate::accountant::scanners::payable_scanner::tx_templates::priced::retry::PricedRetryTxTemplate; use crate::accountant::scanners::payable_scanner::tx_templates::test_utils::RetryTxTemplateBuilder; + use crate::blockchain::blockchain_bridge::increase_by_percentage; #[test] fn constants_are_correct() { @@ -888,7 +889,7 @@ mod tests { let gas_price_wei_from_rpc_u128_wei = u128::from_str_radix(&gas_price_wei_from_rpc_hex[2..], 16).unwrap(); let gas_price_wei_from_rpc_u128_wei_with_margin = - increase_gas_price_by_margin(gas_price_wei_from_rpc_u128_wei); + increase_by_percentage(gas_price_wei_from_rpc_u128_wei); let expected_result = Either::Left(PricedNewTxTemplates::new( tx_templates.clone(), gas_price_wei_from_rpc_u128_wei_with_margin, @@ -906,32 +907,32 @@ mod tests { #[test] fn blockchain_interface_web3_can_introduce_blockchain_agent_in_the_retry_payables_mode() { let gas_price_wei = "0x3B9ACA00"; // 1000000000 - let gas_price_from_rpc = u128::from_str_radix(&gas_price_wei[2..], 16).unwrap(); + let latest_gas_price_wei = u128::from_str_radix(&gas_price_wei[2..], 16).unwrap(); let retry_1 = RetryTxTemplateBuilder::default() .payable_account(&make_payable_account(12)) - .prev_gas_price_wei(gas_price_from_rpc - 1) + .prev_gas_price_wei(latest_gas_price_wei - 1) .build(); let retry_2 = RetryTxTemplateBuilder::default() .payable_account(&make_payable_account(34)) - .prev_gas_price_wei(gas_price_from_rpc) + .prev_gas_price_wei(latest_gas_price_wei) .build(); let retry_3 = RetryTxTemplateBuilder::default() .payable_account(&make_payable_account(56)) - .prev_gas_price_wei(gas_price_from_rpc + 1) + .prev_gas_price_wei(latest_gas_price_wei + 1) .build(); let retry_tx_templates = RetryTxTemplates(vec![retry_1.clone(), retry_2.clone(), retry_3.clone()]); let expected_retry_tx_templates = PricedRetryTxTemplates(vec![ - PricedRetryTxTemplate::new(retry_1, increase_gas_price_by_margin(gas_price_from_rpc)), - PricedRetryTxTemplate::new(retry_2, increase_gas_price_by_margin(gas_price_from_rpc)), + PricedRetryTxTemplate::new(retry_1, increase_by_percentage(latest_gas_price_wei)), + PricedRetryTxTemplate::new(retry_2, increase_by_percentage(latest_gas_price_wei)), PricedRetryTxTemplate::new( retry_3, - increase_gas_price_by_margin(gas_price_from_rpc + 1), + (latest_gas_price_wei + 1) + DEFAULT_GAS_PRICE_RETRY_CONSTANT, ), ]); - let expected_estimated_transaction_fee_total = 285_979_200_073_328; + let expected_estimated_transaction_fee_total = 263_981_166_713_328; test_blockchain_interface_web3_can_introduce_blockchain_agent( Either::Right(retry_tx_templates), diff --git a/node/src/blockchain/blockchain_interface_initializer.rs b/node/src/blockchain/blockchain_interface_initializer.rs index 04838f312..01b97a3b8 100644 --- a/node/src/blockchain/blockchain_interface_initializer.rs +++ b/node/src/blockchain/blockchain_interface_initializer.rs @@ -47,7 +47,7 @@ mod tests { use crate::accountant::scanners::payable_scanner::tx_templates::initial::new::NewTxTemplates; use crate::accountant::scanners::payable_scanner::tx_templates::priced::new::PricedNewTxTemplates; use crate::accountant::test_utils::make_payable_account; - use crate::blockchain::blockchain_bridge::increase_gas_price_by_margin; + use crate::blockchain::blockchain_bridge::increase_by_percentage; use crate::blockchain::blockchain_interface_initializer::BlockchainInterfaceInitializer; use crate::test_utils::make_wallet; use futures::Future; @@ -88,7 +88,7 @@ mod tests { .unwrap(); assert_eq!(blockchain_agent.consuming_wallet(), &payable_wallet); let result = blockchain_agent.price_qualified_payables(Either::Left(tx_templates.clone())); - let gas_price_with_margin = increase_gas_price_by_margin(1_000_000_000); + let gas_price_with_margin = increase_by_percentage(1_000_000_000); let expected_result = Either::Left(PricedNewTxTemplates::new( tx_templates, gas_price_with_margin, From 06dd37499d57594de58cb200c8d1d604cedccb45 Mon Sep 17 00:00:00 2001 From: Bert <65427484+bertllll@users.noreply.github.com> Date: Wed, 17 Dec 2025 13:11:22 +0100 Subject: [PATCH 19/20] GH-598: Receivables scheduling hot fix (#750) --- node/src/accountant/mod.rs | 376 +++++++++--------- node/src/accountant/scanners/mod.rs | 62 ++- .../scanners/payable_scanner/start_scan.rs | 9 +- .../scanners/pending_payable_scanner/mod.rs | 29 +- .../accountant/scanners/scan_schedulers.rs | 283 +++++++++---- node/src/accountant/scanners/test_utils.rs | 35 +- node/src/test_utils/recorder.rs | 7 +- 7 files changed, 472 insertions(+), 329 deletions(-) diff --git a/node/src/accountant/mod.rs b/node/src/accountant/mod.rs index 31f3033b5..85bd9e017 100644 --- a/node/src/accountant/mod.rs +++ b/node/src/accountant/mod.rs @@ -30,7 +30,7 @@ use crate::accountant::scanners::pending_payable_scanner::utils::{ PendingPayableScanResult, TxHashByTable, }; use crate::accountant::scanners::scan_schedulers::{ - PayableSequenceScanner, ScanReschedulingAfterEarlyStop, ScanSchedulers, + ScanReschedulingAfterEarlyStop, ScanSchedulers, UnableToStartScanner, }; use crate::accountant::scanners::{Scanners, StartScanError}; use crate::blockchain::blockchain_bridge::{ @@ -312,11 +312,25 @@ impl Handler for Accountant { impl Handler for Accountant { type Result = (); - fn handle(&mut self, msg: ScanForReceivables, ctx: &mut Self::Context) -> Self::Result { + fn handle(&mut self, msg: ScanForReceivables, _ctx: &mut Self::Context) -> Self::Result { // By now we know it is an automatic scan. The ReceivableScanner is independent of other // scanners and rescheduled regularly, just here. - self.handle_request_of_scan_for_receivable(msg.response_skeleton_opt); - self.scan_schedulers.receivable.schedule(ctx, &self.logger); + let scheduling_hint = self.handle_request_of_scan_for_receivable(msg.response_skeleton_opt); + + match scheduling_hint { + ScanReschedulingAfterEarlyStop::Schedule(other_scan_type) => unreachable!( + "Early stopped receivable scan was suggested to be followed up by the scan \ + for {:?}, which is not supported though", + other_scan_type + ), + ScanReschedulingAfterEarlyStop::DoNotSchedule => { + trace!( + self.logger, + "No early rescheduling, as the receivable scan did find results, or this \ + is the NullScanner" + ) + } + } } } @@ -401,13 +415,19 @@ impl Handler for Accountant { impl Handler for Accountant { type Result = (); - fn handle(&mut self, msg: ReceivedPayments, _ctx: &mut Self::Context) -> Self::Result { - if let Some(node_to_ui_msg) = self.scanners.finish_receivable_scan(msg, &self.logger) { - self.ui_message_sub_opt - .as_ref() - .expect("UIGateway is not bound") - .try_send(node_to_ui_msg) - .expect("UIGateway is dead"); + fn handle(&mut self, msg: ReceivedPayments, ctx: &mut Self::Context) -> Self::Result { + match self.scanners.finish_receivable_scan(msg, &self.logger) { + None => self.scan_schedulers.receivable.schedule(ctx, &self.logger), + Some(node_to_ui_msg) => { + self.ui_message_sub_opt + .as_ref() + .expect("UIGateway is not bound") + .try_send(node_to_ui_msg) + .expect("UIGateway is dead"); + // Externally triggered scans are not allowed to provoke an unwinding scan sequence + // with intervals. The only exception is the PendingPayableScanner that is always + // followed by the retry-payable scanner in a tight tandem. + } } } } @@ -993,8 +1013,8 @@ impl Accountant { .expect("BlockchainBridge is dead"); ScanReschedulingAfterEarlyStop::DoNotSchedule } - Err(e) => self.handle_start_scan_error_and_prevent_scan_stall_point( - PayableSequenceScanner::NewPayables, + Err(e) => self.handle_start_scan_error( + UnableToStartScanner::NewPayables, e, response_skeleton_opt, ), @@ -1025,10 +1045,9 @@ impl Accountant { .expect("BlockchainBridge is dead"); } Err(e) => { - // It is thrown away and there is no rescheduling downstream because every error - // happening here on the start resolves into a panic by the current design - let _ = self.handle_start_scan_error_and_prevent_scan_stall_point( - PayableSequenceScanner::RetryPayables, + // Any error here panics by design, so the return value is unreachable/ignored. + let _ = self.handle_start_scan_error( + UnableToStartScanner::RetryPayables, e, response_skeleton_opt, ); @@ -1063,8 +1082,8 @@ impl Accountant { } Err(e) => { let initial_pending_payable_scan = self.scanners.initial_pending_payable_scan(); - self.handle_start_scan_error_and_prevent_scan_stall_point( - PayableSequenceScanner::PendingPayables { + self.handle_start_scan_error( + UnableToStartScanner::PendingPayables { initial_pending_payable_scan, }, e, @@ -1080,9 +1099,9 @@ impl Accountant { hint } - fn handle_start_scan_error_and_prevent_scan_stall_point( + fn handle_start_scan_error( &self, - scanner: PayableSequenceScanner, + scanner: UnableToStartScanner, e: StartScanError, response_skeleton_opt: Option, ) -> ScanReschedulingAfterEarlyStop { @@ -1109,7 +1128,7 @@ impl Accountant { fn handle_request_of_scan_for_receivable( &mut self, response_skeleton_opt: Option, - ) { + ) -> ScanReschedulingAfterEarlyStop { let result: Result = self.scanners.start_receivable_scan_guarded( &self.earning_wallet, @@ -1120,29 +1139,22 @@ impl Accountant { ); match result { - Ok(scan_message) => self - .retrieve_transactions_sub_opt - .as_ref() - .expect("BlockchainBridge is unbound") - .try_send(scan_message) - .expect("BlockchainBridge is dead"), - Err(e) => { - e.log_error( - &self.logger, - ScanType::Receivables, - response_skeleton_opt.is_some(), - ); - - if let Some(skeleton) = response_skeleton_opt { - self.ui_message_sub_opt - .as_ref() - .expect("UiGateway is unbound") - .try_send(NodeToUiMessage { - target: MessageTarget::ClientId(skeleton.client_id), - body: UiScanResponse {}.tmb(skeleton.context_id), - }) - .expect("UiGateway is dead"); - }; + Ok(scan_message) => { + self.retrieve_transactions_sub_opt + .as_ref() + .expect("BlockchainBridge is unbound") + .try_send(scan_message) + .expect("BlockchainBridge is dead"); + ScanReschedulingAfterEarlyStop::DoNotSchedule + } + Err(e) => + // Any error here panics by design, so the return value is unreachable/ignored. + { + self.handle_start_scan_error( + UnableToStartScanner::Receivables, + e, + response_skeleton_opt, + ) } } } @@ -2289,7 +2301,7 @@ mod tests { let (peer_actors, peer_addresses) = peer_actors_builder() .blockchain_bridge(blockchain_bridge) .ui_gateway(ui_gateway) - .build_and_provide_addresses(); + .build_with_addresses(); let subject_addr = subject.start(); let system = System::new("test"); let response_skeleton_opt = Some(ResponseSkeleton { @@ -2723,6 +2735,40 @@ mod tests { ); } + #[test] + #[should_panic( + expected = "internal error: entered unreachable code: Early stopped receivable scan \ + was suggested to be followed up by the scan for PendingPayables, which is not supported though" + )] + fn start_scan_error_in_receivables_and_unexpected_instruction_from_early_stop_scan_rescheduling( + ) { + let mut subject = AccountantBuilder::default().build(); + let reschedule_on_error_resolver = RescheduleScanOnErrorResolverMock::default() + .resolve_rescheduling_on_error_result(ScanReschedulingAfterEarlyStop::Schedule( + ScanType::PendingPayables, + )); + let receivable_scanner = ScannerMock::default() + .scan_started_at_result(None) + .start_scan_result(Err(StartScanError::NoConsumingWalletFound)); + subject + .scanners + .replace_scanner(ScannerReplacement::Receivable(ReplacementType::Mock( + receivable_scanner, + ))); + subject.scan_schedulers.reschedule_on_error_resolver = + Box::new(reschedule_on_error_resolver); + let system = System::new("test"); + let subject_addr = subject.start(); + + subject_addr + .try_send(ScanForReceivables { + response_skeleton_opt: None, + }) + .unwrap(); + + system.run(); + } + #[test] fn received_payments_with_response_skeleton_sends_response_to_ui_gateway() { let mut config = bc_from_earning_wallet(make_wallet("earning_wallet")); @@ -2732,7 +2778,7 @@ mod tests { receivable_scan_interval: Duration::from_millis(10_000), }); config.automatic_scans_enabled = false; - let subject = AccountantBuilder::default() + let mut subject = AccountantBuilder::default() .bootstrapper_config(config) .config_dao( ConfigDaoMock::new() @@ -2740,6 +2786,9 @@ mod tests { .set_result(Ok(())), ) .build(); + // Another scan must not be scheduled + subject.scan_schedulers.receivable.handle = + Box::new(NotifyLaterHandleMock::default().panic_on_schedule_attempt()); let (ui_gateway, _, ui_gateway_recording_arc) = make_recorder(); let subject_addr = subject.start(); let system = System::new("test"); @@ -2846,11 +2895,13 @@ mod tests { let test_name = "accountant_scans_after_startup_and_does_not_detect_any_pending_payables"; let scan_params = ScanParams::default(); let notify_and_notify_later_params = NotifyAndNotifyLaterParams::default(); - let time_until_next_scan_params_arc = Arc::new(Mutex::new(vec![])); + let compute_time_to_next_scan_params_arc = Arc::new(Mutex::new(vec![])); let earning_wallet = make_wallet("earning"); let consuming_wallet = make_wallet("consuming"); let system = System::new(test_name); - let _ = SystemKillerActor::new(Duration::from_secs(10)).start(); + let (blockchain_bridge, _, _) = make_recorder(); + let blockchain_bridge = blockchain_bridge + .system_stop_conditions(match_lazily_every_type_id!(RetrieveTransactions)); let config = bc_from_wallets(consuming_wallet.clone(), earning_wallet.clone()); let payable_scanner = ScannerMock::new() .scan_started_at_result(None) @@ -2863,25 +2914,31 @@ mod tests { let receivable_scanner = ScannerMock::new() .scan_started_at_result(None) .start_scan_params(&scan_params.receivable_start_scan) - .start_scan_result(Err(StartScanError::NothingToProcess)); - let (subject, new_payable_expected_computed_interval, receivable_scan_interval) = + .start_scan_result(Ok(RetrieveTransactions { + recipient: earning_wallet.clone(), + response_skeleton_opt: None, + })); + let (subject, new_payable_expected_computed_interval) = configure_accountant_for_startup_with_preexisting_pending_payables( test_name, ¬ify_and_notify_later_params, - &time_until_next_scan_params_arc, + &compute_time_to_next_scan_params_arc, config, pending_payable_scanner, receivable_scanner, payable_scanner, ); - let peer_actors = peer_actors_builder().build(); + let peer_actors = peer_actors_builder() + .blockchain_bridge(blockchain_bridge) + .build(); let subject_addr: Addr = subject.start(); let subject_subs = Accountant::make_subs_from(&subject_addr); send_bind_message!(subject_subs, peer_actors); send_start_message!(subject_subs); - // The system is stopped by the NotifyLaterHandleMock for the Receivable scanner + // The system is stopped by the time the RetrieveTransactions msg arrives at the mocked + // BlockchainBridge. let before = SystemTime::now(); system.run(); let after = SystemTime::now(); @@ -2896,15 +2953,13 @@ mod tests { assert_payable_scanner_for_no_pending_payable_found( &scan_params.payable_start_scan, ¬ify_and_notify_later_params, - time_until_next_scan_params_arc, + compute_time_to_next_scan_params_arc, new_payable_expected_computed_interval, ); assert_receivable_scanner( test_name, earning_wallet, &scan_params.receivable_start_scan, - ¬ify_and_notify_later_params.receivables_notify_later, - receivable_scan_interval, ); // The test lays down evidences that the NewPayableScanner couldn't run before // the PendingPayableScanner, which is an intention. @@ -2962,8 +3017,11 @@ mod tests { let receivable_scanner = ScannerMock::new() .scan_started_at_result(None) .start_scan_params(&scan_params.receivable_start_scan) - .start_scan_result(Err(StartScanError::NothingToProcess)); - let (subject, expected_pending_payable_notify_later_interval, receivable_scan_interval) = + .start_scan_result(Ok(RetrieveTransactions { + recipient: earning_wallet.clone(), + response_skeleton_opt: None, + })); + let (subject, expected_pending_payable_notify_later_interval) = configure_accountant_for_startup_with_no_preexisting_pending_payables( test_name, ¬ify_and_notify_later_params, @@ -2972,7 +3030,7 @@ mod tests { pending_payable_scanner, receivable_scanner, ); - let (peer_actors, addresses) = peer_actors_builder().build_and_provide_addresses(); + let (peer_actors, addresses) = peer_actors_builder().build_with_addresses(); let subject_addr: Addr = subject.start(); let subject_subs = Accountant::make_subs_from(&subject_addr); let expected_tx_receipts_msg = TxReceiptsMessage { @@ -3039,8 +3097,6 @@ mod tests { test_name, earning_wallet, &scan_params.receivable_start_scan, - ¬ify_and_notify_later_params.receivables_notify_later, - receivable_scan_interval, ); // Since the assertions proved that the pending payable scanner had run multiple times // before the new payable scanner started or was scheduled, the front position definitely @@ -3065,13 +3121,12 @@ mod tests { new_payables_notify: Arc>>, retry_payables_notify_later: Arc>>, pending_payables_notify_later: Arc>>, - receivables_notify_later: Arc>>, } fn configure_accountant_for_startup_with_preexisting_pending_payables( test_name: &str, notify_and_notify_later_params: &NotifyAndNotifyLaterParams, - time_until_next_scan_params_arc: &Arc>>, + compute_time_to_next_scan_params_arc: &Arc>>, config: BootstrapperConfig, pending_payable_scanner: ScannerMock< RequestTransactionReceipts, @@ -3084,7 +3139,7 @@ mod tests { Option, >, payable_scanner: ScannerMock, - ) -> (Accountant, Duration, Duration) { + ) -> (Accountant, Duration) { let mut subject = make_subject_and_inject_scanners( test_name, config, @@ -3094,7 +3149,6 @@ mod tests { ); let new_payable_expected_computed_interval = Duration::from_secs(3600); // Important that this is made short because the test relies on it with the system stop. - let receivable_scan_interval = Duration::from_millis(50); subject.scan_schedulers.pending_payable.handle = Box::new( NotifyLaterHandleMock::default() .notify_later_params(¬ify_and_notify_later_params.pending_payables_notify_later), @@ -3111,22 +3165,13 @@ mod tests { NotifyHandleMock::default() .notify_params(¬ify_and_notify_later_params.new_payables_notify), ); - let receivable_notify_later_handle_mock = NotifyLaterHandleMock::default() - .notify_later_params(¬ify_and_notify_later_params.receivables_notify_later) - .stop_system_on_count_received(1); - subject.scan_schedulers.receivable.handle = Box::new(receivable_notify_later_handle_mock); - subject.scan_schedulers.receivable.interval = receivable_scan_interval; let interval_computer = NewPayableScanIntervalComputerMock::default() - .time_until_next_scan_params(&time_until_next_scan_params_arc) - .time_until_next_scan_result(ScanTiming::WaitFor( + .compute_time_to_next_scan_params(&compute_time_to_next_scan_params_arc) + .compute_time_to_next_scan_result(ScanTiming::WaitFor( new_payable_expected_computed_interval, )); subject.scan_schedulers.payable.interval_computer = Box::new(interval_computer); - ( - subject, - new_payable_expected_computed_interval, - receivable_scan_interval, - ) + (subject, new_payable_expected_computed_interval) } fn configure_accountant_for_startup_with_no_preexisting_pending_payables( @@ -3144,7 +3189,7 @@ mod tests { ReceivedPayments, Option, >, - ) -> (Accountant, Duration, Duration) { + ) -> (Accountant, Duration) { let mut subject = make_subject_and_inject_scanners( test_name, config, @@ -3152,16 +3197,15 @@ mod tests { receivable_scanner, payable_scanner, ); - let retry_payble_scan_interval = Duration::from_millis(1); + let retry_payable_scan_interval = Duration::from_millis(1); let pending_payable_scan_interval = Duration::from_secs(3600); - let receivable_scan_interval = Duration::from_secs(3600); let pending_payable_notify_later_handle_mock = NotifyLaterHandleMock::default() .notify_later_params(¬ify_and_notify_later_params.pending_payables_notify_later) // This should stop the system .stop_system_on_count_received(1); subject.scan_schedulers.pending_payable.handle = Box::new(pending_payable_notify_later_handle_mock); - subject.scan_schedulers.payable.retry_payable_scan_interval = retry_payble_scan_interval; + subject.scan_schedulers.payable.retry_payable_scan_interval = retry_payable_scan_interval; subject.scan_schedulers.pending_payable.interval = pending_payable_scan_interval; subject.scan_schedulers.payable.new_payable_notify_later = Box::new( NotifyLaterHandleMock::default() @@ -3176,15 +3220,7 @@ mod tests { NotifyHandleMock::default() .notify_params(¬ify_and_notify_later_params.new_payables_notify), ); - let receivable_notify_later_handle_mock = NotifyLaterHandleMock::default() - .notify_later_params(¬ify_and_notify_later_params.receivables_notify_later); - subject.scan_schedulers.receivable.interval = receivable_scan_interval; - subject.scan_schedulers.receivable.handle = Box::new(receivable_notify_later_handle_mock); - ( - subject, - pending_payable_scan_interval, - receivable_scan_interval, - ) + (subject, pending_payable_scan_interval) } fn make_subject_and_inject_scanners( @@ -3485,16 +3521,8 @@ mod tests { receivable_start_scan_params_arc: &Arc< Mutex, Logger, String)>>, >, - scan_for_receivables_notify_later_params_arc: &Arc< - Mutex>, - >, - receivable_scan_interval: Duration, ) { assert_receivable_scan_ran(test_name, receivable_start_scan_params_arc, earning_wallet); - assert_another_receivable_scan_scheduled( - scan_for_receivables_notify_later_params_arc, - receivable_scan_interval, - ) } fn assert_receivable_scan_ran( @@ -3522,25 +3550,6 @@ mod tests { assert_using_the_same_logger(&r_logger, test_name, Some("r")); } - fn assert_another_receivable_scan_scheduled( - scan_for_receivables_notify_later_params_arc: &Arc< - Mutex>, - >, - receivable_scan_interval: Duration, - ) { - let scan_for_receivables_notify_later_params = - scan_for_receivables_notify_later_params_arc.lock().unwrap(); - assert_eq!( - *scan_for_receivables_notify_later_params, - vec![( - ScanForReceivables { - response_skeleton_opt: None - }, - receivable_scan_interval - )] - ); - } - #[test] fn initial_pending_payable_scan_if_some_payables_found() { let sent_payable_dao = @@ -3637,23 +3646,31 @@ mod tests { let start_scan_params_arc = Arc::new(Mutex::new(vec![])); let notify_later_receivable_params_arc = Arc::new(Mutex::new(vec![])); let system = System::new(test_name); + let (blockchain_bridge, _, blockchain_bridge_recording_arc) = make_recorder(); + let blockchain_bridge = blockchain_bridge.system_stop_conditions( + match_lazily_every_type_id!(RetrieveTransactions, RetrieveTransactions), + ); SystemKillerActor::new(Duration::from_secs(10)).start(); // a safety net for GitHub Actions + let recipient = make_wallet("some_recipient"); let receivable_scanner = ScannerMock::new() .scan_started_at_result(None) .scan_started_at_result(None) .start_scan_params(&start_scan_params_arc) - .start_scan_result(Err(StartScanError::NothingToProcess)) .start_scan_result(Ok(RetrieveTransactions { - recipient: make_wallet("some_recipient"), + recipient: recipient.clone(), response_skeleton_opt: None, })) - .stop_the_system_after_last_msg(); + .start_scan_result(Ok(RetrieveTransactions { + recipient: recipient.clone(), + response_skeleton_opt: None, + })) + .finish_scan_result(None); let earning_wallet = make_wallet("earning"); let mut config = bc_from_earning_wallet(earning_wallet.clone()); config.scan_intervals_opt = Some(ScanIntervals { payable_scan_interval: Duration::from_secs(100), pending_payable_scan_interval: Duration::from_secs(10), - receivable_scan_interval: Duration::from_millis(99), + receivable_scan_interval: Duration::from_millis(15), }); let mut subject = AccountantBuilder::default() .bootstrapper_config(config) @@ -3671,8 +3688,25 @@ mod tests { ); let subject_addr = subject.start(); let subject_subs = Accountant::make_subs_from(&subject_addr); - let peer_actors = peer_actors_builder().build(); + let (peer_actors, peer_actors_addrs) = peer_actors_builder() + .blockchain_bridge(blockchain_bridge) + .build_with_addresses(); send_bind_message!(subject_subs, peer_actors); + let counter_msg = ReceivedPayments { + timestamp: SystemTime::now(), + new_start_block: BlockMarker::Value(1234), + transactions: vec![], + response_skeleton_opt: None, + }; + let counter_msg_setup = setup_for_counter_msg_triggered_via_type_id!( + RetrieveTransactions, + counter_msg, + subject_addr + ); + peer_actors_addrs + .blockchain_bridge_addr + .try_send(SetUpCounterMsgs::new(vec![counter_msg_setup])) + .unwrap(); subject_addr .try_send(ScanForReceivables { @@ -3710,37 +3744,37 @@ mod tests { assert!(start_scan_params.is_empty()); debug!( first_attempt_logger, - "first attempt verifying receivable scanner" + "first attempt receivable scanner logger verification" ); debug!( second_attempt_logger, - "second attempt verifying receivable scanner" + "second attempt receivable scanner logger verification" ); + let blockchain_bridge_recording = blockchain_bridge_recording_arc.lock().unwrap(); + let first_msg_towards_bb = + blockchain_bridge_recording.get_record::(0); + let expected_msg = RetrieveTransactions { + recipient, + response_skeleton_opt: None, + }; + assert_eq!(first_msg_towards_bb, &expected_msg); + let second_msg_towards_bb = + blockchain_bridge_recording.get_record::(1); + assert_eq!(second_msg_towards_bb, &expected_msg); assert_eq!( *notify_later_receivable_params, - vec![ - ( - ScanForReceivables { - response_skeleton_opt: None - }, - Duration::from_millis(99) - ), - ( - ScanForReceivables { - response_skeleton_opt: None - }, - Duration::from_millis(99) - ), - ] + vec![( + ScanForReceivables { + response_skeleton_opt: None + }, + Duration::from_millis(15) + ),] ); tlh.exists_log_containing(&format!( - "DEBUG: {test_name}: There was nothing to process during Receivables scan." + "DEBUG: {test_name}: first attempt receivable scanner logger verification", )); tlh.exists_log_containing(&format!( - "DEBUG: {test_name}: first attempt verifying receivable scanner", - )); - tlh.exists_log_containing(&format!( - "DEBUG: {test_name}: second attempt verifying receivable scanner", + "DEBUG: {test_name}: second attempt receivable scanner logger verification", )); } @@ -4215,7 +4249,8 @@ mod tests { expected = "internal error: entered unreachable code: Early stopped new payable scan \ was suggested to be followed up by the scan for Receivables, which is not supported though" )] - fn start_scan_error_in_new_payables_and_unexpected_reaction_by_receivable_scan_scheduling() { + fn start_scan_error_in_new_payables_and_unexpected_instruction_from_early_stop_scan_rescheduling( + ) { let mut subject = AccountantBuilder::default().build(); let reschedule_on_error_resolver = RescheduleScanOnErrorResolverMock::default() .resolve_rescheduling_on_error_result(ScanReschedulingAfterEarlyStop::Schedule( @@ -4435,32 +4470,6 @@ mod tests { ); } - #[test] - #[should_panic( - expected = "internal error: entered unreachable code: Early stopped pending payable scan \ - was suggested to be followed up by the scan for Receivables, which is not supported though" - )] - fn start_scan_error_in_pending_payables_and_unexpected_reaction_by_receivable_scan_scheduling() - { - let mut subject = AccountantBuilder::default().build(); - let reschedule_on_error_resolver = RescheduleScanOnErrorResolverMock::default() - .resolve_rescheduling_on_error_result(ScanReschedulingAfterEarlyStop::Schedule( - ScanType::Receivables, - )); - subject.scan_schedulers.reschedule_on_error_resolver = - Box::new(reschedule_on_error_resolver); - let system = System::new("test"); - let subject_addr = subject.start(); - - subject_addr - .try_send(ScanForPendingPayables { - response_skeleton_opt: None, - }) - .unwrap(); - - system.run(); - } - #[test] fn report_routing_service_provided_message_is_received() { init_test_logging(); @@ -5501,7 +5510,7 @@ mod tests { let test_name = "accountant_confirms_all_pending_txs_and_schedules_new_payable_scanner_timely"; let finish_scan_params_arc = Arc::new(Mutex::new(vec![])); - let time_until_next_scan_params_arc = Arc::new(Mutex::new(vec![])); + let compute_time_to_next_scan_params_arc = Arc::new(Mutex::new(vec![])); let new_payable_notify_later_arc = Arc::new(Mutex::new(vec![])); let new_payable_notify_arc = Arc::new(Mutex::new(vec![])); let system = System::new("new_payable_scanner_timely"); @@ -5518,9 +5527,9 @@ mod tests { ))); let expected_computed_interval = Duration::from_secs(3); let interval_computer = NewPayableScanIntervalComputerMock::default() - .time_until_next_scan_params(&time_until_next_scan_params_arc) + .compute_time_to_next_scan_params(&compute_time_to_next_scan_params_arc) // This determines the test - .time_until_next_scan_result(ScanTiming::WaitFor(expected_computed_interval)); + .compute_time_to_next_scan_result(ScanTiming::WaitFor(expected_computed_interval)); subject.scan_schedulers.payable.interval_computer = Box::new(interval_computer); subject.scan_schedulers.payable.new_payable_notify_later = Box::new( NotifyLaterHandleMock::default().notify_later_params(&new_payable_notify_later_arc), @@ -5578,7 +5587,7 @@ mod tests { let test_name = "accountant_confirms_payable_txs_and_schedules_the_delayed_new_payable_scanner_asap"; let finish_scan_params_arc = Arc::new(Mutex::new(vec![])); - let time_until_next_scan_params_arc = Arc::new(Mutex::new(vec![])); + let compute_time_to_next_scan_params_arc = Arc::new(Mutex::new(vec![])); let new_payable_notify_later_arc = Arc::new(Mutex::new(vec![])); let new_payable_notify_arc = Arc::new(Mutex::new(vec![])); let mut subject = AccountantBuilder::default() @@ -5593,9 +5602,9 @@ mod tests { pending_payable_scanner, ))); let interval_computer = NewPayableScanIntervalComputerMock::default() - .time_until_next_scan_params(&time_until_next_scan_params_arc) + .compute_time_to_next_scan_params(&compute_time_to_next_scan_params_arc) // This determines the test - .time_until_next_scan_result(ScanTiming::ReadyNow); + .compute_time_to_next_scan_result(ScanTiming::ReadyNow); subject.scan_schedulers.payable.interval_computer = Box::new(interval_computer); subject.scan_schedulers.payable.new_payable_notify_later = Box::new( NotifyLaterHandleMock::default().notify_later_params(&new_payable_notify_later_arc), @@ -5630,8 +5639,8 @@ mod tests { "Should be empty but {:?}", finish_scan_params ); - let time_until_next_scan_params = time_until_next_scan_params_arc.lock().unwrap(); - assert_eq!(*time_until_next_scan_params, vec![()]); + let compute_time_to_next_scan_params = compute_time_to_next_scan_params_arc.lock().unwrap(); + assert_eq!(*compute_time_to_next_scan_params, vec![()]); let new_payable_notify_later = new_payable_notify_later_arc.lock().unwrap(); assert!( new_payable_notify_later.is_empty(), @@ -5682,7 +5691,7 @@ mod tests { }), }]); let left_side_bound = if let ScanTiming::WaitFor(interval) = - assertion_interval_computer.time_until_next_scan() + assertion_interval_computer.compute_time_to_next_scan() { interval } else { @@ -5696,7 +5705,7 @@ mod tests { let new_payable_notify_later = new_payable_notify_later_arc.lock().unwrap(); let (_, actual_interval) = new_payable_notify_later[0]; let right_side_bound = if let ScanTiming::WaitFor(interval) = - assertion_interval_computer.time_until_next_scan() + assertion_interval_computer.compute_time_to_next_scan() { interval } else { @@ -5874,8 +5883,9 @@ mod tests { // Setup let notify_later_params_arc = Arc::new(Mutex::new(vec![])); scan_schedulers.payable.interval_computer = Box::new( - NewPayableScanIntervalComputerMock::default() - .time_until_next_scan_result(ScanTiming::WaitFor(Duration::from_secs(152))), + NewPayableScanIntervalComputerMock::default().compute_time_to_next_scan_result( + ScanTiming::WaitFor(Duration::from_secs(152)), + ), ); scan_schedulers.payable.new_payable_notify_later = Box::new( NotifyLaterHandleMock::default().notify_later_params(¬ify_later_params_arc), diff --git a/node/src/accountant/scanners/mod.rs b/node/src/accountant/scanners/mod.rs index cbc337a8d..d14814a5b 100644 --- a/node/src/accountant/scanners/mod.rs +++ b/node/src/accountant/scanners/mod.rs @@ -122,7 +122,10 @@ impl Scanners { }); } - Self::start_correct_payable_scanner::( + <(dyn MultistageDualPayableScanner) as StartableScanner< + ScanForNewPayables, + InitialTemplatesMessage, + >>::start_scan( &mut *self.payable, wallet, timestamp, @@ -150,7 +153,10 @@ impl Scanners { ) } - Self::start_correct_payable_scanner::( + <(dyn MultistageDualPayableScanner) as StartableScanner< + ScanForRetryPayables, + InitialTemplatesMessage, + >>::start_scan( &mut *self.payable, wallet, timestamp, @@ -168,10 +174,14 @@ impl Scanners { automatic_scans_enabled: bool, ) -> Result { let triggered_manually = response_skeleton_opt.is_some(); - self.check_general_conditions_for_pending_payable_scan( - triggered_manually, - automatic_scans_enabled, - )?; + if triggered_manually && automatic_scans_enabled { + return Err(StartScanError::ManualTriggerError( + ManulTriggerError::AutomaticScanConflict, + )); + } + + self.check_pending_payable_existence(triggered_manually)?; + match ( self.pending_payable.scan_started_at(), self.payable.scan_started_at(), @@ -299,41 +309,14 @@ impl Scanners { self.initial_pending_payable_scan = false } - // This is a helper function reducing a boilerplate of complex trait resolving where - // the compiler requires to specify which trigger message distinguishes the scan to run. - // The payable scanner offers two modes through doubled implementations of StartableScanner - // which uses the trigger message type as the only distinction between them. - fn start_correct_payable_scanner<'a, TriggerMessage>( - scanner: &'a mut (dyn MultistageDualPayableScanner + 'a), - wallet: &Wallet, - timestamp: SystemTime, - response_skeleton_opt: Option, - logger: &Logger, - ) -> Result - where - TriggerMessage: Message, - (dyn MultistageDualPayableScanner + 'a): - StartableScanner, - { - <(dyn MultistageDualPayableScanner + 'a) as StartableScanner< - TriggerMessage, - InitialTemplatesMessage, - >>::start_scan(scanner, wallet, timestamp, response_skeleton_opt, logger) - } - - fn check_general_conditions_for_pending_payable_scan( + fn check_pending_payable_existence( &mut self, triggered_manually: bool, - automatic_scans_enabled: bool, ) -> Result<(), StartScanError> { - if triggered_manually && automatic_scans_enabled { - return Err(StartScanError::ManualTriggerError( - ManulTriggerError::AutomaticScanConflict, - )); - } if self.initial_pending_payable_scan { return Ok(()); } + if triggered_manually && !self.aware_of_unresolved_pending_payable { return Err(StartScanError::ManualTriggerError( ManulTriggerError::UnnecessaryRequest { @@ -1343,8 +1326,8 @@ mod tests { #[test] #[should_panic( - expected = "internal error: entered unreachable code: Automatic pending payable \ - scan should never start if there are no pending payables to process." + expected = "internal error: entered unreachable code: Automatic pending payable scan should \ + never start if there are no pending payables to process." )] fn pending_payable_scanner_bumps_into_zero_pending_payable_awareness_in_the_automatic_mode() { let consuming_wallet = make_paying_wallet(b"consuming"); @@ -1363,11 +1346,12 @@ mod tests { } #[test] - fn check_general_conditions_for_pending_payable_scan_if_it_is_initial_pending_payable_scan() { + fn check_pending_payable_existence_for_initial_pending_payable_scan_and_zero_awareness() { let mut subject = make_dull_subject(); + subject.aware_of_unresolved_pending_payable = false; subject.initial_pending_payable_scan = true; - let result = subject.check_general_conditions_for_pending_payable_scan(false, true); + let result = subject.check_pending_payable_existence(false); assert_eq!(result, Ok(())); assert_eq!(subject.initial_pending_payable_scan, true); diff --git a/node/src/accountant/scanners/payable_scanner/start_scan.rs b/node/src/accountant/scanners/payable_scanner/start_scan.rs index 457ab73ee..654580c73 100644 --- a/node/src/accountant/scanners/payable_scanner/start_scan.rs +++ b/node/src/accountant/scanners/payable_scanner/start_scan.rs @@ -35,7 +35,7 @@ impl StartableScanner for PayableSc match qualified_payables.is_empty() { true => { self.mark_as_ended(logger); - Err(StartScanError::NothingToProcess) + Err(StartScanError::nothing_to_process(response_skeleton_opt)) } false => { info!( @@ -89,7 +89,7 @@ mod tests { use crate::accountant::scanners::payable_scanner::tx_templates::initial::retry::{ RetryTxTemplate, RetryTxTemplates, }; - use crate::accountant::scanners::Scanners; + use crate::accountant::scanners::payable_scanner::MultistageDualPayableScanner; use crate::accountant::test_utils::{ make_payable_account, FailedPayableDaoMock, PayableDaoMock, }; @@ -144,7 +144,10 @@ mod tests { .payable_dao(payable_dao) .build(); - let result = Scanners::start_correct_payable_scanner::( + let result = <(dyn MultistageDualPayableScanner) as StartableScanner< + ScanForRetryPayables, + InitialTemplatesMessage, + >>::start_scan( &mut subject, &consuming_wallet, timestamp, diff --git a/node/src/accountant/scanners/pending_payable_scanner/mod.rs b/node/src/accountant/scanners/pending_payable_scanner/mod.rs index 4fe12add1..d3fe5a29d 100644 --- a/node/src/accountant/scanners/pending_payable_scanner/mod.rs +++ b/node/src/accountant/scanners/pending_payable_scanner/mod.rs @@ -95,10 +95,12 @@ impl StartableScanner info!(logger, "Scanning for pending payable"); - let tx_hashes = self.harvest_tables(logger).map_err(|e| { - self.mark_as_ended(logger); - e - })?; + let tx_hashes = self + .harvest_tables(logger, response_skeleton_opt) + .map_err(|e| { + self.mark_as_ended(logger); + e + })?; Ok(RequestTransactionReceipts { tx_hashes, @@ -162,7 +164,11 @@ impl PendingPayableScanner { } } - fn harvest_tables(&mut self, logger: &Logger) -> Result, StartScanError> { + fn harvest_tables( + &mut self, + logger: &Logger, + response_skeleton_opt: Option, + ) -> Result, StartScanError> { debug!(logger, "Harvesting sent_payable and failed_payable tables"); let pending_tx_hashes_opt = self.harvest_pending_payables(); @@ -172,7 +178,7 @@ impl PendingPayableScanner { pending_tx_hashes_opt.as_ref(), failure_hashes_opt.as_ref(), ) { - return Err(StartScanError::NothingToProcess); + return Err(StartScanError::nothing_to_process(response_skeleton_opt)); } Self::log_records_for_receipt_check( @@ -878,7 +884,9 @@ mod tests { }; use crate::accountant::scanners::pending_payable_scanner::PendingPayableScanner; use crate::accountant::scanners::test_utils::PendingPayableCacheMock; - use crate::accountant::scanners::{Scanner, StartScanError, StartableScanner}; + use crate::accountant::scanners::{ + AutomaticError, CommonError, Scanner, StartScanError, StartableScanner, + }; use crate::accountant::test_utils::{ make_transaction_block, FailedPayableDaoMock, PayableDaoMock, PendingPayableScannerBuilder, SentPayableDaoMock, @@ -1230,7 +1238,12 @@ mod tests { let result = subject.start_scan(&consuming_wallet, now, None, &Logger::new("test")); let is_scan_running = subject.scan_started_at().is_some(); - assert_eq!(result, Err(StartScanError::NothingToProcess)); + assert_eq!( + result, + Err(StartScanError::Automatic(AutomaticError::Common( + CommonError::NothingToProcess + ))) + ); assert_eq!(is_scan_running, false); } diff --git a/node/src/accountant/scanners/scan_schedulers.rs b/node/src/accountant/scanners/scan_schedulers.rs index 97a3edd62..5de151c73 100644 --- a/node/src/accountant/scanners/scan_schedulers.rs +++ b/node/src/accountant/scanners/scan_schedulers.rs @@ -51,28 +51,31 @@ pub enum ScanReschedulingAfterEarlyStop { } #[derive(Debug, PartialEq, Eq, Clone, Copy)] -pub enum PayableSequenceScanner { +pub enum UnableToStartScanner { NewPayables, RetryPayables, PendingPayables { initial_pending_payable_scan: bool }, + Receivables, } -impl Display for PayableSequenceScanner { +impl Display for UnableToStartScanner { fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result { match self { - PayableSequenceScanner::NewPayables => write!(f, "NewPayables"), - PayableSequenceScanner::RetryPayables => write!(f, "RetryPayables"), - PayableSequenceScanner::PendingPayables { .. } => write!(f, "PendingPayables"), + UnableToStartScanner::NewPayables => write!(f, "NewPayables"), + UnableToStartScanner::RetryPayables => write!(f, "RetryPayables"), + UnableToStartScanner::PendingPayables { .. } => write!(f, "PendingPayables"), + UnableToStartScanner::Receivables => write!(f, "Receivables"), } } } -impl From for ScanType { - fn from(scanner: PayableSequenceScanner) -> Self { +impl From for ScanType { + fn from(scanner: UnableToStartScanner) -> Self { match scanner { - PayableSequenceScanner::NewPayables => ScanType::Payables, - PayableSequenceScanner::RetryPayables => ScanType::Payables, - PayableSequenceScanner::PendingPayables { .. } => ScanType::PendingPayables, + UnableToStartScanner::NewPayables => ScanType::Payables, + UnableToStartScanner::RetryPayables => ScanType::Payables, + UnableToStartScanner::PendingPayables { .. } => ScanType::PendingPayables, + UnableToStartScanner::Receivables => ScanType::Receivables, } } } @@ -99,7 +102,7 @@ impl PayableScanScheduler { } pub fn schedule_new_payable_scan(&self, ctx: &mut Context, logger: &Logger) { - if let ScanTiming::WaitFor(interval) = self.interval_computer.time_until_next_scan() { + if let ScanTiming::WaitFor(interval) = self.interval_computer.compute_time_to_next_scan() { debug!( logger, "Scheduling a new-payable scan in {}ms", @@ -153,7 +156,7 @@ impl PayableScanScheduler { } pub trait NewPayableScanIntervalComputer { - fn time_until_next_scan(&self) -> ScanTiming; + fn compute_time_to_next_scan(&self) -> ScanTiming; fn reset_last_scan_timestamp(&mut self); @@ -167,7 +170,7 @@ pub struct NewPayableScanIntervalComputerReal { } impl NewPayableScanIntervalComputer for NewPayableScanIntervalComputerReal { - fn time_until_next_scan(&self) -> ScanTiming { + fn compute_time_to_next_scan(&self) -> ScanTiming { let current_time = self.clock.now(); let time_since_last_scan = current_time .duration_since(self.last_scan_timestamp) @@ -250,7 +253,7 @@ where pub trait RescheduleScanOnErrorResolver { fn resolve_rescheduling_on_error( &self, - scanner: PayableSequenceScanner, + scanner: UnableToStartScanner, error: &StartScanError, is_externally_triggered: bool, logger: &Logger, @@ -263,25 +266,28 @@ pub struct RescheduleScanOnErrorResolverReal {} impl RescheduleScanOnErrorResolver for RescheduleScanOnErrorResolverReal { fn resolve_rescheduling_on_error( &self, - scanner: PayableSequenceScanner, + scanner: UnableToStartScanner, error: &StartScanError, is_externally_triggered: bool, logger: &Logger, ) -> ScanReschedulingAfterEarlyStop { let reschedule_hint = match scanner { - PayableSequenceScanner::NewPayables => { + UnableToStartScanner::NewPayables => { Self::resolve_new_payables(error, is_externally_triggered) } - PayableSequenceScanner::RetryPayables => { + UnableToStartScanner::RetryPayables => { Self::resolve_retry_payables(error, is_externally_triggered) } - PayableSequenceScanner::PendingPayables { + UnableToStartScanner::PendingPayables { initial_pending_payable_scan, } => Self::resolve_pending_payables( error, initial_pending_payable_scan, is_externally_triggered, ), + UnableToStartScanner::Receivables => { + Self::resolve_receivables(error, is_externally_triggered) + } }; Self::log_rescheduling(scanner, is_externally_triggered, logger, &reschedule_hint); @@ -296,14 +302,18 @@ impl RescheduleScanOnErrorResolverReal { is_externally_triggered: bool, ) -> ScanReschedulingAfterEarlyStop { if is_externally_triggered { - ScanReschedulingAfterEarlyStop::DoNotSchedule - } else if matches!(err, StartScanError::ScanAlreadyRunning { .. }) { - unreachable!( - "an automatic scan of NewPayableScanner should never interfere with itself {:?}", - err - ) - } else { - ScanReschedulingAfterEarlyStop::Schedule(ScanType::Payables) + return ScanReschedulingAfterEarlyStop::DoNotSchedule; + } + + match err { + StartScanError::CalledFromNullScanner => ScanReschedulingAfterEarlyStop::DoNotSchedule, + StartScanError::ScanAlreadyRunning { .. } => { + unreachable!( + "an automatic scan of NewPayableScanner should never interfere with itself {:?}", + err + ) + } + _ => ScanReschedulingAfterEarlyStop::Schedule(ScanType::Payables), } } @@ -330,18 +340,26 @@ impl RescheduleScanOnErrorResolverReal { is_externally_triggered: bool, ) -> ScanReschedulingAfterEarlyStop { if is_externally_triggered { - ScanReschedulingAfterEarlyStop::DoNotSchedule - } else if err == &StartScanError::NothingToProcess { - if initial_pending_payable_scan { + return ScanReschedulingAfterEarlyStop::DoNotSchedule; + } + + match err { + StartScanError::NothingToProcess => { + if !initial_pending_payable_scan { + unreachable!( + "the automatic pending payable scan should always be requested only in need, \ + which contradicts the current StartScanError::NothingToProcess" + ) + } ScanReschedulingAfterEarlyStop::Schedule(ScanType::Payables) - } else { - unreachable!( - "the automatic pending payable scan should always be requested only in need, \ - which contradicts the current StartScanError::NothingToProcess" - ) } - } else if err == &StartScanError::NoConsumingWalletFound { - if initial_pending_payable_scan { + StartScanError::NoConsumingWalletFound => { + if !initial_pending_payable_scan { + unreachable!( + "PendingPayableScanner called later than the initial attempt, but \ + the consuming wallet is still missing; this should not be possible" + ) + } // Cannot deduce there are strayed pending payables from the previous Node's run // (StartScanError::NoConsumingWalletFound is thrown before // StartScanError::NothingToProcess can be evaluated); but may be cautious and @@ -350,22 +368,34 @@ impl RescheduleScanOnErrorResolverReal { // TODO Correctly, a check-point during the bootstrap, not allowing to come // this far, should be the solution. Part of the issue mentioned in GH-799 ScanReschedulingAfterEarlyStop::Schedule(ScanType::PendingPayables) - } else { - unreachable!( - "PendingPayableScanner called later than the initial attempt, but \ - the consuming wallet is still missing; this should not be possible" - ) } - } else { - unreachable!( + StartScanError::CalledFromNullScanner => ScanReschedulingAfterEarlyStop::DoNotSchedule, + _ => unreachable!( "{:?} should be impossible with PendingPayableScanner in automatic mode", err - ) + ), + } + } + + fn resolve_receivables( + err: &StartScanError, + is_externally_triggered: bool, + ) -> ScanReschedulingAfterEarlyStop { + if is_externally_triggered { + return ScanReschedulingAfterEarlyStop::DoNotSchedule; + } + + match err { + StartScanError::CalledFromNullScanner => ScanReschedulingAfterEarlyStop::DoNotSchedule, + _ => unreachable!( + "{:?} should be impossible with ReceivableScanner in automatic mode", + err + ), } } fn log_rescheduling( - scanner: PayableSequenceScanner, + scanner: UnableToStartScanner, is_externally_triggered: bool, logger: &Logger, reschedule_hint: &ScanReschedulingAfterEarlyStop, @@ -389,8 +419,8 @@ impl RescheduleScanOnErrorResolverReal { #[cfg(test)] mod tests { use crate::accountant::scanners::scan_schedulers::{ - NewPayableScanIntervalComputer, NewPayableScanIntervalComputerReal, PayableSequenceScanner, - ScanReschedulingAfterEarlyStop, ScanSchedulers, ScanTiming, + NewPayableScanIntervalComputer, NewPayableScanIntervalComputerReal, + ScanReschedulingAfterEarlyStop, ScanSchedulers, ScanTiming, UnableToStartScanner, }; use crate::accountant::scanners::test_utils::NewPayableScanIntervalComputerMock; use crate::accountant::scanners::{ManulTriggerError, StartScanError}; @@ -469,7 +499,7 @@ mod tests { subject.scan_interval = standard_interval; subject.last_scan_timestamp = past_instant; - let result = subject.time_until_next_scan(); + let result = subject.compute_time_to_next_scan(); assert_eq!( result, @@ -504,7 +534,7 @@ mod tests { subject.scan_interval = standard_interval; subject.last_scan_timestamp = past_instant; - let result = subject.time_until_next_scan(); + let result = subject.compute_time_to_next_scan(); assert_eq!( result, @@ -524,7 +554,7 @@ mod tests { subject.scan_interval = Duration::from_secs(180); subject.clock = Box::new(SimpleClockMock::default().now_result(now)); - let result = subject.time_until_next_scan(); + let result = subject.compute_time_to_next_scan(); assert_eq!( result, @@ -570,7 +600,7 @@ mod tests { subject.clock = Box::new(SimpleClockMock::default().now_result(now)); subject.last_scan_timestamp = now.checked_add(Duration::from_secs(1)).unwrap(); - let _ = subject.time_until_next_scan(); + let _ = subject.compute_time_to_next_scan(); } #[test] @@ -648,7 +678,6 @@ mod tests { StartScanError::CalledFromNullScanner ]; - let mut check_vec = candidates .iter() .fold(vec![],|mut acc, current|{ @@ -709,14 +738,14 @@ mod tests { test_what_if_externally_triggered( &format!("{}(initial_pending_payable_scan = false)", test_name), &subject, - PayableSequenceScanner::PendingPayables { + UnableToStartScanner::PendingPayables { initial_pending_payable_scan: false, }, ); test_what_if_externally_triggered( &format!("{}(initial_pending_payable_scan = true)", test_name), &subject, - PayableSequenceScanner::PendingPayables { + UnableToStartScanner::PendingPayables { initial_pending_payable_scan: true, }, ); @@ -725,7 +754,7 @@ mod tests { fn test_what_if_externally_triggered( test_name: &str, subject: &ScanSchedulers, - scanner: PayableSequenceScanner, + scanner: UnableToStartScanner, ) { init_test_logging(); let logger = Logger::new(test_name); @@ -755,17 +784,16 @@ mod tests { } #[test] - fn resolve_error_for_pending_payables_if_nothing_to_process_and_initial_pending_payable_scan_true( - ) { + fn resolve_error_if_nothing_to_process_and_initial_pending_payable_scan_true() { init_test_logging(); let subject = ScanSchedulers::new(*TEST_SCAN_INTERVALS, true); - let test_name = "resolve_error_for_pending_payables_if_nothing_to_process_and_initial_pending_payable_scan_true"; + let test_name = "resolve_error_if_nothing_to_process_and_initial_pending_payable_scan_true"; let logger = Logger::new(test_name); let result = subject .reschedule_on_error_resolver .resolve_rescheduling_on_error( - PayableSequenceScanner::PendingPayables { + UnableToStartScanner::PendingPayables { initial_pending_payable_scan: true, }, &StartScanError::NothingToProcess, @@ -798,7 +826,7 @@ mod tests { let _ = subject .reschedule_on_error_resolver .resolve_rescheduling_on_error( - PayableSequenceScanner::PendingPayables { + UnableToStartScanner::PendingPayables { initial_pending_payable_scan: false, }, &StartScanError::NothingToProcess, @@ -808,12 +836,13 @@ mod tests { } #[test] - fn resolve_error_for_pending_p_if_no_consuming_wallet_found_in_initial_pending_payable_scan() { + fn resolve_error_if_no_consuming_wallet_found_in_initial_pending_payable_scan() { init_test_logging(); - let test_name = "resolve_error_for_pending_p_if_no_consuming_wallet_found_in_initial_pending_payable_scan"; + let test_name = + "resolve_error_if_no_consuming_wallet_found_in_initial_pending_payable_scan"; let logger = Logger::new(test_name); let subject = ScanSchedulers::new(*TEST_SCAN_INTERVALS, true); - let scanner = PayableSequenceScanner::PendingPayables { + let scanner = UnableToStartScanner::PendingPayables { initial_pending_payable_scan: true, }; @@ -845,9 +874,10 @@ mod tests { than the initial attempt, but the consuming wallet is still missing; this should not be \ possible" )] - fn pending_p_scan_attempt_if_no_consuming_wallet_found_mustnt_happen_if_not_initial_scan() { + fn pending_payable_scan_attempt_if_no_consuming_wallet_found_mustnt_happen_if_not_initial_scan() + { let subject = ScanSchedulers::new(*TEST_SCAN_INTERVALS, true); - let scanner = PayableSequenceScanner::PendingPayables { + let scanner = UnableToStartScanner::PendingPayables { initial_pending_payable_scan: false, }; @@ -861,6 +891,20 @@ mod tests { ); } + #[test] + fn resolve_error_for_pending_payables_if_null_scanner_is_used_in_automatic_mode() { + test_resolve_error_if_null_scanner_is_used_in_automatic_mode( + UnableToStartScanner::PendingPayables { + initial_pending_payable_scan: true, + }, + ); + test_resolve_error_if_null_scanner_is_used_in_automatic_mode( + UnableToStartScanner::PendingPayables { + initial_pending_payable_scan: false, + }, + ); + } + #[test] fn resolve_error_for_pending_payables_forbidden_states() { fn test_forbidden_states( @@ -873,7 +917,7 @@ mod tests { subject .reschedule_on_error_resolver .resolve_rescheduling_on_error( - PayableSequenceScanner::PendingPayables { + UnableToStartScanner::PendingPayables { initial_pending_payable_scan, }, *error, @@ -900,6 +944,7 @@ mod tests { let inputs = ListOfStartScanErrors::default().eliminate_already_tested_variants(vec![ StartScanError::NothingToProcess, StartScanError::NoConsumingWalletFound, + StartScanError::CalledFromNullScanner, ]); let subject = ScanSchedulers::new(*TEST_SCAN_INTERVALS, true); @@ -916,7 +961,7 @@ mod tests { test_what_if_externally_triggered( test_name, &subject, - PayableSequenceScanner::RetryPayables {}, + UnableToStartScanner::RetryPayables {}, ); } @@ -929,7 +974,7 @@ mod tests { subject .reschedule_on_error_resolver .resolve_rescheduling_on_error( - PayableSequenceScanner::RetryPayables, + UnableToStartScanner::RetryPayables, error, false, &Logger::new("test"), @@ -952,15 +997,14 @@ mod tests { } #[test] - fn resolve_rescheduling_on_error_works_for_new_payables_if_externally_triggered() { - let test_name = - "resolve_rescheduling_on_error_works_for_new_payables_if_externally_triggered"; + fn resolve_rescheduling_on_error_for_new_payables_if_externally_triggered() { + let test_name = "resolve_rescheduling_on_error_for_new_payables_if_externally_triggered"; let subject = ScanSchedulers::new(*TEST_SCAN_INTERVALS, true); test_what_if_externally_triggered( test_name, &subject, - PayableSequenceScanner::NewPayables {}, + UnableToStartScanner::NewPayables {}, ); } @@ -969,13 +1013,13 @@ mod tests { expected = "internal error: entered unreachable code: an automatic scan of NewPayableScanner \ should never interfere with itself ScanAlreadyRunning { cross_scan_cause_opt: None, started_at:" )] - fn resolve_hint_for_new_payables_if_scan_is_already_running_error_and_is_automatic_scan() { + fn resolve_hint_for_new_payables_error_if_scan_is_already_running_in_automatic_scan() { let subject = ScanSchedulers::new(*TEST_SCAN_INTERVALS, true); let _ = subject .reschedule_on_error_resolver .resolve_rescheduling_on_error( - PayableSequenceScanner::NewPayables, + UnableToStartScanner::NewPayables, &StartScanError::ScanAlreadyRunning { cross_scan_cause_opt: None, started_at: SystemTime::now(), @@ -985,10 +1029,18 @@ mod tests { ); } + #[test] + fn resolve_error_for_new_payables_if_null_scanner_is_used_in_automatic_mode() { + test_resolve_error_if_null_scanner_is_used_in_automatic_mode( + UnableToStartScanner::NewPayables, + ); + } + #[test] fn resolve_new_payables_with_error_cases_resulting_in_future_rescheduling() { let test_name = "resolve_new_payables_with_error_cases_resulting_in_future_rescheduling"; let inputs = ListOfStartScanErrors::default().eliminate_already_tested_variants(vec![ + StartScanError::CalledFromNullScanner, StartScanError::ScanAlreadyRunning { cross_scan_cause_opt: None, started_at: SystemTime::now(), @@ -1002,7 +1054,7 @@ mod tests { let result = subject .reschedule_on_error_resolver .resolve_rescheduling_on_error( - PayableSequenceScanner::NewPayables, + UnableToStartScanner::NewPayables, *error, false, &logger, @@ -1021,27 +1073,100 @@ mod tests { }) } + #[test] + fn resolve_rescheduling_on_error_for_receivables_if_externally_triggered() { + let test_name = "resolve_rescheduling_on_error_for_receivables_if_externally_triggered"; + let subject = ScanSchedulers::new(*TEST_SCAN_INTERVALS, true); + + test_what_if_externally_triggered(test_name, &subject, UnableToStartScanner::Receivables); + } + + #[test] + fn resolve_error_for_receivables_if_null_scanner_is_used_in_automatic_mode() { + test_resolve_error_if_null_scanner_is_used_in_automatic_mode( + UnableToStartScanner::Receivables, + ); + } + + #[test] + fn resolve_error_for_receivables_all_fatal_cases_in_automatic_mode() { + let inputs = ListOfStartScanErrors::default() + .eliminate_already_tested_variants(vec![StartScanError::CalledFromNullScanner]); + let subject = ScanSchedulers::new(*TEST_SCAN_INTERVALS, true); + + inputs.errors.iter().for_each(|error| { + let panic = catch_unwind(AssertUnwindSafe(|| { + subject + .reschedule_on_error_resolver + .resolve_rescheduling_on_error( + UnableToStartScanner::Receivables, + *error, + false, + &Logger::new("test"), + ) + })) + .unwrap_err(); + + let panic_msg = panic.downcast_ref::().unwrap(); + let expected_msg = format!( + "internal error: entered unreachable code: {:?} should be impossible with \ + ReceivableScanner in automatic mode", + error + ); + assert_eq!( + panic_msg, &expected_msg, + "We expected '{}' but got '{}'", + expected_msg, panic_msg + ) + }) + } + #[test] fn conversion_between_hintable_scanner_and_scan_type_works() { assert_eq!( - ScanType::from(PayableSequenceScanner::NewPayables), + ScanType::from(UnableToStartScanner::NewPayables), ScanType::Payables ); assert_eq!( - ScanType::from(PayableSequenceScanner::RetryPayables), + ScanType::from(UnableToStartScanner::RetryPayables), ScanType::Payables ); assert_eq!( - ScanType::from(PayableSequenceScanner::PendingPayables { + ScanType::from(UnableToStartScanner::PendingPayables { initial_pending_payable_scan: false }), ScanType::PendingPayables ); assert_eq!( - ScanType::from(PayableSequenceScanner::PendingPayables { + ScanType::from(UnableToStartScanner::PendingPayables { initial_pending_payable_scan: true }), ScanType::PendingPayables ); + assert_eq!( + ScanType::from(UnableToStartScanner::Receivables), + ScanType::Receivables + ) + } + + fn test_resolve_error_if_null_scanner_is_used_in_automatic_mode(scanner: UnableToStartScanner) { + let subject = ScanSchedulers::new(*TEST_SCAN_INTERVALS, true); + + let result = subject + .reschedule_on_error_resolver + .resolve_rescheduling_on_error( + scanner, + &StartScanError::CalledFromNullScanner, + false, + &Logger::new("test"), + ); + + assert_eq!( + result, + ScanReschedulingAfterEarlyStop::DoNotSchedule, + "We expected DoNotSchedule but got {:?} for {:?}", + result, + scanner + ); } } diff --git a/node/src/accountant/scanners/test_utils.rs b/node/src/accountant/scanners/test_utils.rs index 08325dedc..031e1bb7d 100644 --- a/node/src/accountant/scanners/test_utils.rs +++ b/node/src/accountant/scanners/test_utils.rs @@ -18,8 +18,8 @@ use crate::accountant::scanners::pending_payable_scanner::{ CachesEmptiableScanner, ExtendedPendingPayablePrivateScanner, }; use crate::accountant::scanners::scan_schedulers::{ - NewPayableScanIntervalComputer, PayableSequenceScanner, RescheduleScanOnErrorResolver, - ScanReschedulingAfterEarlyStop, ScanTiming, + NewPayableScanIntervalComputer, RescheduleScanOnErrorResolver, ScanReschedulingAfterEarlyStop, + ScanTiming, UnableToStartScanner, }; use crate::accountant::scanners::{ PendingPayableScanner, PrivateScanner, RealScannerMarker, ReceivableScanner, Scanner, @@ -336,15 +336,20 @@ impl ScannerMockMarker for ScannerMock>>, - time_until_next_scan_results: RefCell>, + compute_time_to_next_scan_params: Arc>>, + compute_time_to_next_scan_results: RefCell>, reset_last_scan_timestamp_params: Arc>>, } impl NewPayableScanIntervalComputer for NewPayableScanIntervalComputerMock { - fn time_until_next_scan(&self) -> ScanTiming { - self.time_until_next_scan_params.lock().unwrap().push(()); - self.time_until_next_scan_results.borrow_mut().remove(0) + fn compute_time_to_next_scan(&self) -> ScanTiming { + self.compute_time_to_next_scan_params + .lock() + .unwrap() + .push(()); + self.compute_time_to_next_scan_results + .borrow_mut() + .remove(0) } fn reset_last_scan_timestamp(&mut self) { @@ -358,13 +363,15 @@ impl NewPayableScanIntervalComputer for NewPayableScanIntervalComputerMock { } impl NewPayableScanIntervalComputerMock { - pub fn time_until_next_scan_params(mut self, params: &Arc>>) -> Self { - self.time_until_next_scan_params = params.clone(); + pub fn compute_time_to_next_scan_params(mut self, params: &Arc>>) -> Self { + self.compute_time_to_next_scan_params = params.clone(); self } - pub fn time_until_next_scan_result(self, result: ScanTiming) -> Self { - self.time_until_next_scan_results.borrow_mut().push(result); + pub fn compute_time_to_next_scan_result(self, result: ScanTiming) -> Self { + self.compute_time_to_next_scan_results + .borrow_mut() + .push(result); self } @@ -470,14 +477,14 @@ pub fn assert_timestamps_from_str(examined_str: &str, expected_timestamps: Vec>>, + Arc>>, resolve_rescheduling_on_error_results: RefCell>, } impl RescheduleScanOnErrorResolver for RescheduleScanOnErrorResolverMock { fn resolve_rescheduling_on_error( &self, - scanner: PayableSequenceScanner, + scanner: UnableToStartScanner, error: &StartScanError, is_externally_triggered: bool, logger: &Logger, @@ -500,7 +507,7 @@ impl RescheduleScanOnErrorResolver for RescheduleScanOnErrorResolverMock { impl RescheduleScanOnErrorResolverMock { pub fn resolve_rescheduling_on_error_params( mut self, - params: &Arc>>, + params: &Arc>>, ) -> Self { self.resolve_rescheduling_on_error_params = params.clone(); self diff --git a/node/src/test_utils/recorder.rs b/node/src/test_utils/recorder.rs index f52b1a0c8..569a5f804 100644 --- a/node/src/test_utils/recorder.rs +++ b/node/src/test_utils/recorder.rs @@ -641,8 +641,9 @@ impl PeerActorsBuilder { } // This must be called after System.new and before System.run. - // These addresses may be helpful for setting up the Counter Messages. - pub fn build_and_provide_addresses(self) -> (PeerActors, PeerActorAddrs) { + // + // The addresses may be helpful for setting up the Counter Messages. + pub fn build_with_addresses(self) -> (PeerActors, PeerActorAddrs) { let proxy_server_addr = self.proxy_server.start(); let dispatcher_addr = self.dispatcher.start(); let hopper_addr = self.hopper.start(); @@ -683,7 +684,7 @@ impl PeerActorsBuilder { // This must be called after System.new and before System.run pub fn build(self) -> PeerActors { - let (peer_actors, _) = self.build_and_provide_addresses(); + let (peer_actors, _) = self.build_with_addresses(); peer_actors } } From 422038f17e08b604aab3c1c803197a48ba153b22 Mon Sep 17 00:00:00 2001 From: Bert Date: Wed, 17 Dec 2025 20:22:35 +0100 Subject: [PATCH 20/20] immediate fix for screwed GH-598-receivables-scheduling-hot-fix --- .../scanners/payable_scanner/start_scan.rs | 2 +- .../scanners/pending_payable_scanner/mod.rs | 29 +++++-------------- 2 files changed, 9 insertions(+), 22 deletions(-) diff --git a/node/src/accountant/scanners/payable_scanner/start_scan.rs b/node/src/accountant/scanners/payable_scanner/start_scan.rs index 654580c73..2684407f7 100644 --- a/node/src/accountant/scanners/payable_scanner/start_scan.rs +++ b/node/src/accountant/scanners/payable_scanner/start_scan.rs @@ -35,7 +35,7 @@ impl StartableScanner for PayableSc match qualified_payables.is_empty() { true => { self.mark_as_ended(logger); - Err(StartScanError::nothing_to_process(response_skeleton_opt)) + Err(StartScanError::NothingToProcess) } false => { info!( diff --git a/node/src/accountant/scanners/pending_payable_scanner/mod.rs b/node/src/accountant/scanners/pending_payable_scanner/mod.rs index d3fe5a29d..4fe12add1 100644 --- a/node/src/accountant/scanners/pending_payable_scanner/mod.rs +++ b/node/src/accountant/scanners/pending_payable_scanner/mod.rs @@ -95,12 +95,10 @@ impl StartableScanner info!(logger, "Scanning for pending payable"); - let tx_hashes = self - .harvest_tables(logger, response_skeleton_opt) - .map_err(|e| { - self.mark_as_ended(logger); - e - })?; + let tx_hashes = self.harvest_tables(logger).map_err(|e| { + self.mark_as_ended(logger); + e + })?; Ok(RequestTransactionReceipts { tx_hashes, @@ -164,11 +162,7 @@ impl PendingPayableScanner { } } - fn harvest_tables( - &mut self, - logger: &Logger, - response_skeleton_opt: Option, - ) -> Result, StartScanError> { + fn harvest_tables(&mut self, logger: &Logger) -> Result, StartScanError> { debug!(logger, "Harvesting sent_payable and failed_payable tables"); let pending_tx_hashes_opt = self.harvest_pending_payables(); @@ -178,7 +172,7 @@ impl PendingPayableScanner { pending_tx_hashes_opt.as_ref(), failure_hashes_opt.as_ref(), ) { - return Err(StartScanError::nothing_to_process(response_skeleton_opt)); + return Err(StartScanError::NothingToProcess); } Self::log_records_for_receipt_check( @@ -884,9 +878,7 @@ mod tests { }; use crate::accountant::scanners::pending_payable_scanner::PendingPayableScanner; use crate::accountant::scanners::test_utils::PendingPayableCacheMock; - use crate::accountant::scanners::{ - AutomaticError, CommonError, Scanner, StartScanError, StartableScanner, - }; + use crate::accountant::scanners::{Scanner, StartScanError, StartableScanner}; use crate::accountant::test_utils::{ make_transaction_block, FailedPayableDaoMock, PayableDaoMock, PendingPayableScannerBuilder, SentPayableDaoMock, @@ -1238,12 +1230,7 @@ mod tests { let result = subject.start_scan(&consuming_wallet, now, None, &Logger::new("test")); let is_scan_running = subject.scan_started_at().is_some(); - assert_eq!( - result, - Err(StartScanError::Automatic(AutomaticError::Common( - CommonError::NothingToProcess - ))) - ); + assert_eq!(result, Err(StartScanError::NothingToProcess)); assert_eq!(is_scan_running, false); }